为什么在关联类型上不识别除第一个之外的超级特征边界?
Why are supertrait bounds other than the first not recognized on an associated type?
此代码片段在 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
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`
我读到这个错误是因为编译器没有识别出 AddAssign<Self>
是为 T::Associated
实现的,这显然是错误的,因为它实现了 Trait
,这需要 AddAssign<Self>
.
如果我们更改 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
,为什么不能识别另一个?看来最后一界才是要被认出来的
我的第一个怀疑是这与动态调度有关。
我排除了它,因为即使在动态调度中,边界的顺序也不重要。我什至不认为它会使用它,因为在编译时使用单态化已知所有类型。
我目前的怀疑是一个编译器错误,当它是关联类型时,类型检查器不考虑特征边界上的泛型。很容易想象这样一个特定的案例被忽视了。
这是怎么回事?
这是一个已知错误(或几个错误的组合):
- Higher-ranked trait bounds on associated types are not elaborated (#50346).
- where clauses are only elaborated for supertraits, and not other things (#20671)
- Constraints on associated types declared in subtraits do not propagate. (#32722)
- Unrecognized associated type bound on another associated type (#24159)
解决方法是在每个使用站点重申边界:
fn func<T2>()
where
T: Trait2,
T::Associated: Trait,
{
let mut t = T::method(1);
let t2 = T::method(2);
t += &t2;
t += t2;
}
这应该得到解决 when the type system moves from its ad hoc implementation to Chalk,这是一个针对复杂类型系统产生的问题类型的更有原则的解决方案。
此代码片段在 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
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`
我读到这个错误是因为编译器没有识别出 AddAssign<Self>
是为 T::Associated
实现的,这显然是错误的,因为它实现了 Trait
,这需要 AddAssign<Self>
.
如果我们更改 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
,为什么不能识别另一个?看来最后一界才是要被认出来的
我的第一个怀疑是这与动态调度有关。 我排除了它,因为即使在动态调度中,边界的顺序也不重要。我什至不认为它会使用它,因为在编译时使用单态化已知所有类型。
我目前的怀疑是一个编译器错误,当它是关联类型时,类型检查器不考虑特征边界上的泛型。很容易想象这样一个特定的案例被忽视了。
这是怎么回事?
这是一个已知错误(或几个错误的组合):
- Higher-ranked trait bounds on associated types are not elaborated (#50346).
- where clauses are only elaborated for supertraits, and not other things (#20671)
- Constraints on associated types declared in subtraits do not propagate. (#32722)
- Unrecognized associated type bound on another associated type (#24159)
解决方法是在每个使用站点重申边界:
fn func<T2>()
where
T: Trait2,
T::Associated: Trait,
{
let mut t = T::method(1);
let t2 = T::method(2);
t += &t2;
t += t2;
}
这应该得到解决 when the type system moves from its ad hoc implementation to Chalk,这是一个针对复杂类型系统产生的问题类型的更有原则的解决方案。