I've attempted use an impl to handle some strange type logic. Here is a quick reconstruction of the bug:
trait Schrodingers {}
struct AliveCat;
impl Schrodingers for Container<AliveCat> {}
struct DeadCat;
impl Schrodingers for Container<DeadCat> {}
struct Container<Cat1>
where Container<Cat1>: Schrodingers
{
cat: Cat1,
}
impl<Cat2> Container<Cat2>
where Container<Cat2>: Schrodingers
{
fn dead_cat() -> Container<DeadCat> {
let observed_cat = DeadCat;
Container { cat: observed_cat }
}
fn alive_cat() -> Container<AliveCat> {
let observed_cat = AliveCat;
Container { cat: observed_cat }
}
}
fn main() {
let dead_cat = Container::dead_cat();
let alive_cat = Container::alive_cat();
}
Which results in the compiler errors:
error[E0308]: mismatched types
--> src/main.rs:19:26
|
19 | Container { cat: observed_cat }
| ^^^^^^^^^^^^ expected type parameter, found struct `DeadCat`
|
= note: expected type `Cat2`
= note: found type `DeadCat`
error[E0308]: mismatched types
--> src/main.rs:19:9
|
19 | Container { cat: observed_cat }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `DeadCat`, found type parameter
|
= note: expected type `Container<DeadCat>`
= note: found type `Container<Cat2>`
error[E0308]: mismatched types
--> src/main.rs:24:26
|
24 | Container { cat: observed_cat }
| ^^^^^^^^^^^^ expected type parameter, found struct `AliveCat`
|
= note: expected type `Cat2`
= note: found type `AliveCat`
error[E0308]: mismatched types
--> src/main.rs:24:9
|
24 | Container { cat: observed_cat }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `AliveCat`, found type parameter
|
= note: expected type `Container<AliveCat>`
= note: found type `Container<Cat2>`
I have been able to solve this using other methods, but why would the compiler find this confusing?
The compiler was "put against the wall" so to speak.
Container<Cat>is bound to implementSchrodingersdirectly in the struct declaration.An impl block for
Container<C>follows it with the same trait bound.And finally, we have associated function, such as
alive_cat, which exists for everyCatinContainer<Cat>whereContainer<Cat>: Schrodingers, yet disregards the type parameterCat, returning aContainer<AliveCat>.The compiler then appears to mistakenly infer the parameter type
Catas the specificAliveCat, even though it does not necessarily true that everyCatis alwaysAliveCatin thatimplblock, leading to a weird inconsistency, and a misleading error message. As far as the compiler's capabilities are concerned, this could probably work with some tweaks to Rustc, but they might not necessarily happen.Fortunately, there are easy development guidelines to follow that prevent this issue from ever occurring:
implclauses instead. Otherwise, any bounds in the struct have to be applied to all subsequent impl blocks, even if that bound is not particularly relevant to the items implemented.Self(or something containingSelf, such asResult<Self, _>, etc). This forces you to write an independentimplblock for that constructor, with no generic type involved.See also: