-
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
Inherent/trait method priority rules exist, but are unclear and seem to be undocumented #26007
Comments
@nikomatsakis is this some kind of bug? |
/cc @rust-lang/lang , I repeat my question from last time: is this a bug, or are there rules? We should doc the rules if this is expected. |
IMO what's being described here is a subtle but correct combination of autoderef and priority rules. |
Ugh I would prefer it if this were not the case but don't know what would break! In particular, I would prefer if |
I believe that what ever the rules for this were they recently changed in beta ( and earlier on in nightly ) I have a travis build that just started failing on beta do the incorrect selection of dispatch https://travis-ci.org/softprops/afterparty/jobs/188728497#L378 I think I have precisely the case listed in this issue. I have an inherent impl method called handle which takes a &mut self and impl a trait ( hyper's Handler ) method, also called called on stable the prioritization was my inherent impl and on beta/nighlightly the priority has become the trait impl. I worked around this with UFCS in softprops/afterparty@2b15483 but I think this change may come as a surprise to some. It may very well be the case that my usage has worked by accident but I think the lack of docs on expectation for this may hurt others as well. |
what's wierd is that if I modify the playpen example above to try and reproduce the error I see in my build it fails on stable as well in the playpen https://play.rust-lang.org/?gist=ac3bb4233c2c32876ff68f0fcd9c222d&version=stable&backtrace=1 |
The priority table in my original post seems to still be the same on |
Mysterious |
I found what I think my by a work around for my case with scoping. As long as the trait with a conflicting method is not in scope things work ask expected. Another case for avoiding glob-style imports! Here's an example. |
I've never commented on this bug it seems. These rules are indeed intentional and yes they are the result of an interplay between autoderef rules and prioritization. The algorithm is that we first arrange a series of steps which correspond to increasing amounts of deref operations. We will try these steps in order and stop at the first step for which we find methods. Within a given step, we will give inherent methods priority over trait methods. This ordering is the result of a lot of experimentation and making changes to it will definitely cause massive breakage. Before 1.0, I did spend some time experimenting with different "cleaner" strategies, but abandoned it because they broke a lot of code and by and large made things work less well (i.e., more explicit The exact algorithm:
I am certainly not saying that this procedure is ideal, but we definitely can't cavalierly change it, and it has some nice properties. For example, if you have an An example of a change I tried to make which did not work was to search for impls defined directly on the type X and then apply the autoref by looking at how the method was defined. This seems cleaner, but it interacted quite poorly with |
So basically I think we have to document these rules but I wouldn't want to change them without careful consideration. |
In a docs team meeting we decided this would be best served in the reference, so I'm moving the issue over there: rust-lang-nursery/reference#62 |
I just ran into a surprising (to me at least) consequence of the current rules: capnproto/capnproto-rust#90 I have a struct |
@dwrensha while it can happen that trait methods get preferred, I'm a bit confused by the wya you described the situation. For example, in this self-contained example, the inherent method is chosen. |
@nikomatsakis oops! I forgot about a impl <'a> Foo<'a> {
borrow<'b>(&'b mut self) -> Foo<'b> { ... }
} If I add a |
I see. That does match my expectations, for better or worse -- in particular, the method selection algorithm first tries no-ref, then shared-ref ( It seems to me that it's sort of late to make any changes here for the current epoch -- but I feel like an epochal transition is exactly where we might try to make improvements. |
Consider this code:
Unlike a method name conflict between two traits, Rust will compile this code, which will produce:
One might conclude that the inherent method (i.e. belonging to the type itself)
Foo::foo()
has priority over the trait methodBar::foo()
. Yet, if we change the signature ofFoo::foo()
tofn foo(&mut self)
in this code, suddenly the program outputs:So it would seem the type of arguments also plays some role in which method is chosen. To try and understand the rules behind it, I created the following table which shows the output for each combination of
self
's type inFoo::foo()
andBar::foo()
:Looking at this, I think I can deduce that:
&mut Self
<&Self
<Self
.That said, these rules are not terribly intuitive, and I'm unsure how they stack up against more complex method signatures. It would be good to have documentation about this.
Another concern of mine, however, is the inconsistency between this and a situation involving multiple trait methods conflicting. In that situation, rustc gives an error and asks the programmer to specify which trait's method should be used via UFCS. I'd prefer something like that over the current situation, though I'm not sure it's possible to do backwards-compatibly.
The text was updated successfully, but these errors were encountered: