Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
526 views
in Technique[技术] by (71.8m points)

generics - Why are supertrait bounds other than the first not recognized on an associated type?

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 AddAssigns, 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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This is a known bug (or a combination of a few):

  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)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...