This snippet is valid in Rust 1.26.1:
use std::ops::AddAssign;
trait Trait
where
for<'a> Self: AddAssign<Self> + AddAssign<&'a Self> + Sized,
{
}
trait Trait2 {
type Associated: Trait;
fn method(u32) -> Self::Associated;
}
fn func<T2: Trait2>() {
let mut t = T2::method(1);
let t2 = T2::method(2);
t += &t2;
}
Notice that Trait
implements both AddAssign<Self>
and AddAssign<&'a Trait>
(in that order, which is important later). Therefore, in func
we know that both t += t2
and t += &t2
should be valid. As seen on the playground, t += &t2
is valid, but using t += t2
isn't:
error[E0308]: mismatched types
--> src/main.rs:19:10
|
19 | t += t2;
| ^^
| |
| expected reference, found associated type
| help: consider borrowing here: `&t2`
|
= note: expected type `&<T2 as Trait2>::Associated`
found type `<T2 as Trait2>::Associated`
I read this error as the compiler not recognizing that AddAssign<Self>
is implemented for T::Associated
, which is clearly wrong, as it implements Trait
, which requires AddAssign<Self>
.
If we change the order of the AddAssign
bounds on Trait
then the opposite holds: t += t2
is valid while t += &t2
isn't.
A quick fix for the problem is to make func
generic over both traits:
fn func<T: Trait, T2: Trait2<Associated = T>>() {
let mut t = T2::method(1);
let t2 = T2::method(2);
t += t2;
}
This shouldn't be necessary; the compiler can recognize one of the AddAssign
s, why not the other? It seems the last bound is the one to be recognized.
My first suspicion was this this has something to do with dynamic dispatch. I ruled it out since the order of the bounds shouldn't matter even in dynamic dispatch. I don't even think it uses it, since all types are known at compile-time using monomorphisation.
My current suspicion is a compiler bug where the typechecker doesn't account for generics on trait bounds when it is an associated type. It is easy to imagine such a specific case being overlooked.
What is going here?
This is a known bug (or a combination of a few):
The workaround is to restate the bounds at every usage site:
fn func<T2>()
where
T: Trait2,
T::Associated: Trait,
{
let mut t = T::method(1);
let t2 = T::method(2);
t += &t2;
t += t2;
}
This should be addressed when the type system moves from its ad hoc implementation to Chalk, a more principled solver for the types of problems a complicated type system creates.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With