此代码段在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;
}
请注意,Trait
同时实现了AddAssign<Self>
和AddAssign<&'a Trait>
(按此顺序,稍后将重点介绍)。因此,在func
中,我们知道t += t2
和t += &t2
都应该有效。如on the playground所示,t += &t2
是有效的,但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`
我把这个错误理解为编译器没有认识到AddAssign<Self>
是为T::Associated
实现的,这显然是错误的,因为它实现了需要AddAssign<Self>
的Trait
。
如果我们更改Trait
上AddAssign
界限的顺序,则相反的情况成立:t += t2
is valid while t += &t2
isn't。
这个问题的一个快速解决方案是将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;
}
这应该不是必需的;编译器可以识别其中一个AddAssign
,为什么不能识别另一个呢?似乎最后一个界限才是被认可的。
我的第一个怀疑是,这与动态调度有关。我排除了它,因为即使在动态调度中,边界的顺序也不应该重要。我甚至不认为它使用它,因为所有类型在编译时都是已知的,使用单形化。
我现在怀疑是一个编译器错误,当类型检查器是关联类型时,类型检查器不会考虑特征边界上的泛型。很容易想象这样一个特定的案例会被忽视。
这是怎么回事?
发布于 2018-06-03 07:30:40
这是一个已知的错误(或几个错误的组合):
解决方法是重申每个使用地点的界限:
fn func<T2>()
where
T: Trait2,
T::Associated: Trait,
{
let mut t = T::method(1);
let t2 = T::method(2);
t += &t2;
t += t2;
}
从它的特殊实现到Chalk,这都应该得到解决,对于复杂的类型系统所产生的问题,它是一个更有原则的解决方案。
https://stackoverflow.com/questions/50660911
复制相似问题