Rust: Nested associated type projection is overly conservative

Created on 30 Nov 2016  路  20Comments  路  Source: rust-lang/rust

The following setup

trait OneTrait {
    type Ty: AnotherTrait;
    fn project() -> Self::Ty::Out;
}

trait AnotherTrait {
    type Out;
}

produces the error

rustc 1.13.0 (2c6933acc 2016-11-07)
error[E0223]: ambiguous associated type
 --> <anon>:3:17
  |
3 |     fn bar() -> Self::Bar::Out;
  |                 ^^^^^^^^^^^^^^ ambiguous associated type
  |
  = note: specify the type using the syntax `<<Self as Foo>::Bar as Trait>::Out`

It would be great for the less explicit projection to work here.

cc @nikomatsakis

A-traits C-bug

Most helpful comment

Has there been any progress on this front? Was just curious if there was/will be/is planned to have an update of some sort.

All 20 comments

Note that the issue title may be overly specific; I haven't deeply investigated the cases where this kind of projection fails.

In my experience at least A::B::C never works, although maybe bugs have been fixed that enables that recnetly?

FWIW, <<Self as Foo>::Bar as Trait>::Out can be shortened to <Self::Ty as AnotherTrait>::Out.
A::B for associated types currently works only if A is a type parameter (including Self) that is known to have a trait with associated type B as a bound.

In terms of the compiler's implementation, this isn't really about projection. It's not actually getting that far. Rather, it fails when lowering the AST into types, which is the point where we have to pick a trait. @eddyb has been doing a lot of heroic work refactoring the type-end to remove that limitation, which is fairly tricky -- the problem is that to do it properly, one must reason about the where-clauses that are in scope and do trait selection, but to know what where-clauses are in scope, one must lower them, and that is what we are trying to do. To resolve this, @eddyb has been refactoring the compiler to cope with partially lowered sets of where-clauses, basically.

So, is @eddyb's fix for this being developed openly somewhere it's possible to track progress (or help?) This issue is causing me some frustration, so I'd love to know what I could do to help resolve it.

I think he wound up taking another approach. I'm not sure what's the shortest path here just now.

Perhaps keying the type_param_predicates requests on a type instead of just type parameters specifically, and treating failure to convert some types in bounds as an ambiguity, may work.

This is a _huge_ paper cut for me: I have a lot of code of the form <<<<F as E>::E as D>::D as C>::C as B>::B as A>::A> and it is completely unreadable to write methods on traits while having to skim through that.

There are two cases where this cuts me:

  • when I know that there isn't an ambiguity yet I have to disambiguate
  • when I doubt if there is an ambiguity and might not want one, in which case I need to go through the whole trait hierarchy looking for it because... even though the error says "ambiguous" it doesn't bother telling me where the ambiguities are...

Having to disambiguate is the smaller paper cut. It would already be infinitely helpful if the error message would tell me if there is an actual ambiguity and where, or if it would tell me that there isn't an ambiguity.

Maybe the problem is that what I understand by ambiguous (e.g. that two associated types are named equal) and what the compiler understands by ambiguous are different things.

I've tried using syntax, that one would expect to work just fine, and have wound up with this issue. It's quite clear to any human that there exist no ambiguities in this code, yet the compiler unnecessarily requires extra (and ugly) syntax to accept the code. This is extremely inconvenient.

I'm not sure if chiming in and saying "me too" at this point is more annoying than helpful, but me too :)

I think this one's hard on novice Rustaceans because we're interpreting the situation as human error, as if we've unintentionally made the situation ambiguous and there's something we can do to correct it.

I would also love this. I've been "shortening" it like this, but this way there is an extra associated type that structs have to implement for no reason:

trait OneTrait {
    type Ty: AnotherTrait<Out=Self::Oy>;
    type Oy;
    fn project() -> Self::Oy;
}

trait AnotherTrait {
    type Out;
}

At the very least, the error message should be improved; it gives bad advice:
[Playground]

Is this issue one that someone could take on as a first issue for fixing the compiler or is this still in the area of experts? It seemed like something quite hard a while ago, but maybe progress has been made? If this is something easier than it used to be, could someone point me to the area of code that would need to be fixed?

Sadly, not enough progress has been made for this to be readily doable yet.

Has there been any progress on this front? Was just curious if there was/will be/is planned to have an update of some sort.

Will chalk resolve this?

Still happens with Rust 1.45.2

Following code:

pub struct MyStruct {
    rng : rand::Rng::SmallRng
}

Outputs:

 error[E0223]: ambiguous associated type
    --> src\algorithm\m_struct.rs:11:11
       |
    11 |     rng : rand::Rng::SmallRng,
       |           ^^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<(dyn rand::Rng + 'static) as Trait>::SmallRng`

Is there any workaround?

chalk will not resolve this without more work, though we did have some experimental ideas for how to resolve it.

Was this page helpful?
0 / 5 - 0 ratings