Module rustc::middle::typeck::check::method[src]

Method lookup

Method lookup can be rather complex due to the interaction of a number of factors, such as self types, autoderef, trait lookup, etc. The algorithm is divided into two parts: candidate collection and candidate selection.

Candidate collection

A Candidate is a method item that might plausibly be the method being invoked. Candidates are grouped into two kinds, inherent and extension. Inherent candidates are those that are derived from the type of the receiver itself. So, if you have a receiver of some nominal type Foo (e.g., a struct), any methods defined within an impl like impl Foo are inherent methods. Nothing needs to be imported to use an inherent method, they are associated with the type itself (note that inherent impls can only be defined in the same module as the type itself).

Inherent candidates are not always derived from impls. If you have a trait instance, such as a value of type Box<ToStr>, then the trait methods (to_str(), in this case) are inherently associated with it. Another case is type parameters, in which case the methods of their bounds are inherent.

Extension candidates are derived from imported traits. If I have the trait ToStr imported, and I call to_str() on a value of type T, then we will go off to find out whether there is an impl of ToStr for T. These kinds of method calls are called "extension methods". They can be defined in any module, not only the one that defined T. Furthermore, you must import the trait to call such a method.

For better or worse, we currently give weight to inherent methods over extension methods during candidate selection (below).

Candidate selection

Once we know the set of candidates, we can go off and try to select which one is actually being called. We do this by taking the type of the receiver, let's call it R, and checking whether it matches against the expected receiver type for each of the collected candidates. We first check for inherent candidates and see whether we get exactly one match (zero means keep searching, more than one is an error). If so, we return that as the candidate. Otherwise we search the extension candidates in the same way.

If find no matching candidate at all, we proceed to auto-deref the receiver type and search again. We keep doing that until we cannot auto-deref any longer. At each step, we also check for candidates based on "autoptr", which if the current type is T, checks for &mut T, &const T, and &T receivers. Finally, at the very end, we will also try autoslice, which converts ~[] to &[] (there is no point at trying autoslice earlier, because no autoderefable type is also sliceable).

Why two phases?

You might wonder why we first collect the candidates and then select. Both the inherent candidate collection and the candidate selection proceed by progressively deref'ing the receiver type, after all. The answer is that two phases are needed to elegantly deal with explicit self. After all, if there is an impl for the type Foo, it can define a method with the type Box<self>, which means that it expects a receiver of type Box<Foo>. If we have a receiver of type Box<Foo>, but we waited to search for that impl until we have deref'd the Box away and obtained the type Foo, we would never match this method.

Enums

AutoderefReceiverFlag
CheckTraitsFlag
RcvrMatchCondition

This type represents the conditions under which the receiver is considered to "match" a given method candidate. Typically the test is whether the receiver is of a particular type. However, this type is the type of the receiver after accounting for the method's self type (e.g., if the method is an Box<self> method, we have already verified that the receiver is of some type Box<T> and now we must check that the type T is correct). Unfortunately, because traits are not types, this is a pain to do.

StaticMethodsFlag

Functions

lookup
lookup_in_trait