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 implementSchrodingers
directly 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 everyCat
inContainer<Cat>
whereContainer<Cat>: Schrodingers
, yet disregards the type parameterCat
, returning aContainer<AliveCat>
.The compiler then appears to mistakenly infer the parameter type
Cat
as the specificAliveCat
, even though it does not necessarily true that everyCat
is alwaysAliveCat
in thatimpl
block, 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:
impl
clauses 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 independentimpl
block for that constructor, with no generic type involved.See also: