-
Notifications
You must be signed in to change notification settings - Fork 13.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Permit unqualified references to associated types of the trait within the trait definition #18764
Comments
Also: #![feature(associated_types)]
pub trait Transfer<S> {
type R;
fn transfer(&mut self) -> R;
}
fn main(){}
|
Updated title for precision. |
This makes refactoring code to use associated types a lot more painful, and adds another straw to the camel's back of associated types being less ergonomic than |
There's been some debate on whether this feature should go forward. The main objection, I believe, is that the RFC brings into scope all items of the trait itself, but not any super traits. This is partly due to hygiene concerns, and partly due to the fact that the notion of a "super trait" is somewhat blurred with generalized Personally, I think the ergonomic and readability benefits are worth it even if in edge cases the |
Nominating for beta milestone: we need to at least make a decision on this topic before then. |
Was that supposed to be one of the goals of moving to associated types? I have no idea but would have expected it was more about expressivity (with some cost in terms of ergonomics). In any case, I'm sort of on the fence about this one. I think I find having the |
@darinmorrison it would be nice if the common case for generics didn't become harder, regardless of explicit goals. Especially with new Orphan/Impl checks forcing associated types. |
Whatever decision we make here should apply to |
In the scope of
The programmer can't even write |
Similarly in impls, if we have |
One thing is that it would be inconsistent with referencing non-type members: trait Foo {
type Bar;
fn m() -> Self { .. }
fn n() {
Foo::Bar // OK
Foo::m(); // error, need to infer self-type
}
} |
After some discussion, the plan is (which we believe conforms to the RFC): Within a trait In the future, we expect that Note that within |
cc me |
After discussing with @eddyb on irc, it sounds like skipping the So, a revised spec: in any trait or impl, where This has the downside (as previously discussed in person) that referring to an associated type in a super trait requires the (I actually have this (as previously spec'ed) working for associated types, but was having some difficulty with methods. It sounds like I'd be better off rebasing or starting again over @eddyb's UFCS branch. Using his partial resolution abstraction for these things sounds like a nice way forward). |
leaving on 1.0 beta list, P-backcompat-lang. (i.e. we want the new behavior outlined in the bug description.) |
A key question here is what to do where an associated item could shadow an outer item, e.g.,
There are two options: we prefer the non-shorthand type of the module-level The general implementation strategy here is that when resolving a path, we take note of whether If this all worked, then the shadowing behaviour would fall out nicely. However, there are situations where even testing for However, this seems kind of bad - by changing a parameter on a bound of super-trait (or something equally remote) we can cause name resolution errors in fairly unconnected places. Furthermore, whether you can use a The alternative is a slightly weird scoping rule that non-associative items in an outer scope shadow unqualified, associated items from an inner scope. This breaks local reasoning somewhat, i.e., you need to know the entire context of an item to know whether you can use the shorthand or not. And adding an item in an arbitrary place can break the use of a shorthand (imagine a glob import in an outer module). OTOH, at least you don't need to know about bounds and super-traits. The implementation is also much simpler - you only need to worry about the Note that relative to current Rust, the first solution is backwards incompatible and the second is backwards compatible. If we adopt the second solution, changing to the first is a backwards incompatible change. |
So, questions, given the above, do we think it is worth the effort to adopt 'proper' shadowing of associated types, or is the 'associated types have lowest precedence' model sufficient? Does everyone still think having some shorthand is the way forward? |
Can you provide a minimal example of this problem? I'm having a hard time quite following the interactions here, or finding the relevant bit of trait Foo: Bar {}
trait Bar: Foo {} fails to compile for this reason. So it seems that it should be possible to walk up the explicit trait hierarchy and discover all of the relevant items to bring into scope, and AFAIK this shouldn't involve bounds, but maybe I'm missing something?
I think the "associated types have lowest precedence" option is a non-starter, personally: it's inconsistent with our general shadowing rules, and as you say loses the local reasoning about binding. I would rather forgo bringing the items into scope at all. (However, I still hope we can make the original semantics work out.) |
@aturon, the relevant part of
Note that the cycle is not a simple one, but is via a type parameter. We don't need to resolve 'deeply' for normal type collection, so this is Ok. However, if we try to resolve By 'original semantics' do you mean the rules by which the scope of an associated type follows from |
Thanks for the clarification!
So, I fear I'm still missing something here. My expectation would be that the names brought into scope due to
Oh, sorry, I meant the semantics in #18764 (comment), which I believe is what you, @nikomatsakis and I discussed a while back. My understanding was that for beta we would focus on the items defined within the trait itself, and try to include supertrait methods as quickly as possible after that. I don't think I'm aware of the default method issues you're alluding to here, was that a recent discovery? It sounds as if you've already abandoned the semantics we had discussed? If so, it seems I'm rather out of the loop :) |
I think in an ideal world that is true (about the names brought into scope), but I think we need to know the type parameters in order to properly know those names. This might just be for the impl case - that is, inside an impl, The idea that we would just look at associated items in the current trait is what I call the syntactic approach. It comes down to only looking at the associated items which are lexically declared in the current impl or trait. I think it is still feasible, but I was persuaded that it was even less optimal that we thought and that the semantic approach (with either kind of scoping) was easier than I originally thought (ha!). Methods are an issue because expectations are a bit different, I think, it seems even more weird that whether you can write |
I don't think I understand the cycle that @nrc is describing yet. Perhaps it's too early and I've not had enough coffee. However, the reason that we sometimes do want to know more type parameters is because we compile an assoc type reference like At some point @nrc and I discussed the idea of doing something that is ... mostly right now and then either reporting errors or having a known bug for the corner cases, to be resolved as we rework resolve. Not sure how viable that is right now. |
For the record I feel...fairly uncomfortable with a scoping rule that puts assoc type resolution as a fallback. It only seems like something we would consider because of the way that the impl works, I feel like it's not something we'd devise from first principles. |
We decided at the weekly meeting today that we would stick with requiring |
Gives the error:
According to the RFC the
Result
ought to refer to the associated type.The text was updated successfully, but these errors were encountered: