除了第一个未在相关类型上识别的超级界限之外,为什么?

时间:2018-06-02 20:29:35

标签: generics rust traits associated-types

此代码段在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 += t2t += &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实现的,这显然是错误的,因为它实现了Trait,这需要AddAssign<Self>。< / p>

如果我们更改AddAssignTrait边界的顺序,则相反的情况为:t += t2 is validt += &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,为什么不识别另一个?似乎最后一个被认可的是。

我的第一个怀疑是这与动态调度有关。 我排除了它,因为即使在动态调度中,边界的顺序也不重要。我甚至认为它不会使用它,因为所有类型都是在编译时使用单态化来知道的。

我目前的怀疑是编译器错误,当类型检查器是关联类型时,它不会考虑特征限制的泛型。很容易想象这样一个特定的案例被忽视了。

这是怎么回事?

1 个答案:

答案 0 :(得分:4)

这是一个已知错误(或少数几个的组合):

  1. Higher-ranked trait bounds on associated types are not elaborated (#50346)
  2. where clauses are only elaborated for supertraits, and not other things (#20671)
  3. Constraints on associated types declared in subtraits do not propagate. (#32722)
  4. Unrecognized associated type bound on another associated type (#24159)
  5. 解决方法是重申每个使用地点的界限:

    fn func<T2>()
    where
        T: Trait2,
        T::Associated: Trait,
    {
        let mut t = T::method(1);
        let t2 = T::method(2);
        t += &t2;
        t += t2;
    }
    

    这应该从{em> ad hoc 实现when the type system moves Chalk解决,https://docs.unity3d.com/ScriptReference/Mathf.Epsilon.html是复杂类型系统创建的问题类型的更原则的解决方案。