【问题标题】:Why are supertrait bounds other than the first not recognized on an associated type?为什么除了第一个之外的超特征边界在关联类型上不被识别?
【发布时间】:2018-11-12 15:04:14
【问题描述】:

这个 sn-p 在 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&lt;Self&gt; 和 AddAssign&lt;&amp;'a Trait&gt;(按此顺序,这在后面很重要)。因此,在func 中,我们知道t += t2 和t += &amp;t2 都应该是有效的。如看到on the playground,t += &amp;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&lt;Self&gt; 是为T::Associated 实现的,这显然是错误的,因为它实现了Trait,它需要AddAssign&lt;Self&gt;。

如果我们更改Trait 上的AddAssign 边界的顺序,则相反的情况成立:t += t2 is valid 而t += &amp;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;
}

这不应该是必要的;编译器可以识别AddAssigns 之一,为什么不能识别另一个?似乎最后一个界限是被识别的界限。

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

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

这是怎么回事?

【问题讨论】:

  • 我从未在文档中看到任何内容表明顺序应该很重要,所以这感觉像是一个错误。
  • “动态调度。我什至不认为它使用它” — 正确。这里没有动态调度。

标签: generics rust traits associated-types


【解决方案1】:

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

  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)

解决方法是在每个使用站点重述界限:

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 从它的ad hoc 实现到Chalk 解决,这是一个更原则性的解决复杂类型系统所产生问题类型的解决方案。

【讨论】:

    猜你喜欢
    • 2022-11-18
    • 2019-12-17
    • 1970-01-01
    • 1970-01-01
    • 2016-10-18
    • 2020-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多