-
Notifications
You must be signed in to change notification settings - Fork 13.8k
Description
I tried this code:
pub struct Test<const FLAG: bool>;
pub trait Supertrait {}
pub trait Subtrait: Supertrait {}
impl Supertrait for Test<true> {}
impl Supertrait for Test<false> {}
impl<const FLAG: bool> Subtrait for Test<FLAG> {}
I expected the compiler to accept the implementation for Subtrait
because Supertrait
is implemented for every possible value of FLAG
.
Instead, it cannot unify these implementations and gives an error:
error[E0277]: the trait bound `Test<FLAG>: Supertrait` is not satisfied
--> src/lib.rs:7:37
|
7 | impl<const FLAG: bool> Subtrait for Test<FLAG> {}
| ^^^^^^^^^^ the trait `Supertrait` is not implemented for `Test<FLAG>`
|
= help: the following other types implement trait `Supertrait`:
Test<false>
Test<true>
note: required by a bound in `Subtrait`
--> src/lib.rs:3:21
|
3 | pub trait Subtrait: Supertrait {}
| ^^^^^^^^^^ required by this bound in `Subtrait`
For more information about this error, try `rustc --explain E0277`.
Meta
rustc --version --verbose
:
rustc 1.92.0-nightly (9f32ccf35 2025-09-21)
binary: rustc
commit-hash: 9f32ccf35fb877270bc44a86a126440f04d676d0
commit-date: 2025-09-21
host: x86_64-unknown-linux-gnu
release: 1.92.0-nightly
LLVM version: 21.1.1
Other remarks
This form of "exhaustive unification" is probably undesirable for things like integers (since even 256 trait implementations is quite a lot), but it is reasonable for boolean generics and will likely be desired as well once adt_const_params
(tracked in #95174) is stable. In fact, there will probably be some desire to use ADT const generics to replace booleans for readability, so, this case doesn't seem too far-fetched.
Note that it is possible to make these cases much more complicated by adding two, three, or more boolean arguments, and those should be dealt with similarly.
Less-convoluted example
I'm personally interested in using this for the sake of avoiding macros in trait implementations, because it's immediately apparent why this is useful if you add associated types, for example:
pub trait Supertrait {
type Error: Default;
}
pub trait Subtrait: Supertrait {
fn do_thing(self) -> Result<Self, <Self as Supertrait>::Error>;
}
(this is still convoluted, but at least helpful to show the justification here)