Method-call expressions
Syntax
MethodCallExpression :
Expression.
PathExprSegment(
CallParams?)
A method call consists of an expression (the receiver) followed by a single dot, an expression path segment, and a parenthesized expression-list.
Method calls are resolved to associated methods on specific traits, either statically dispatching to a method if the exact self
-type of the left-hand-side is known, or dynamically dispatching if the left-hand-side expression is an indirect trait object.
#![allow(unused)] fn main() { let pi: Result<f32, _> = "3.14".parse(); let log_pi = pi.unwrap_or(1.0).log(2.72); assert!(1.14 < log_pi && log_pi < 1.15) }
When looking up a method call, the receiver may be automatically dereferenced or borrowed in order to call a method. This requires a more complex lookup process than for other functions, since there may be a number of possible methods to call. The following procedure is used:
The first step is to build a list of candidate receiver types.
Obtain these by repeatedly dereferencing the receiver expression's type, adding each type encountered to the list, then finally attempting an unsized coercion at the end, and adding the result type if that is successful.
Then, for each candidate T
, add &T
and &mut T
to the list immediately after T
.
For instance, if the receiver has type Box<[i32;2]>
, then the candidate types will be Box<[i32;2]>
, &Box<[i32;2]>
, &mut Box<[i32;2]>
, [i32; 2]
(by dereferencing), &[i32; 2]
, &mut [i32; 2]
, [i32]
(by unsized coercion), &[i32]
, and finally &mut [i32]
.
Then, for each candidate type T
, search for a visible method with a receiver of that type in the following places:
T
's inherent methods (methods implemented directly onT
).- Any of the methods provided by a visible trait implemented by
T
. IfT
is a type parameter, methods provided by trait bounds onT
are looked up first. Then all remaining methods in scope are looked up.
Note: the lookup is done for each type in order, which can occasionally lead to surprising results. The below code will print "In trait impl!", because
&self
methods are looked up first, the trait method is found before the struct's&mut self
method is found.struct Foo {} trait Bar { fn bar(&self); } impl Foo { fn bar(&mut self) { println!("In struct impl!") } } impl Bar for Foo { fn bar(&self) { println!("In trait impl!") } } fn main() { let mut f = Foo{}; f.bar(); }
If this results in multiple possible candidates, then it is an error, and the receiver must be converted to an appropriate receiver type to make the method call.
This process does not take into account the mutability or lifetime of the receiver, or whether a method is unsafe
.
Once a method is looked up, if it can't be called for one (or more) of those reasons, the result is a compiler error.
If a step is reached where there is more than one possible method, such as where generic methods or traits are considered the same, then it is a compiler error. These cases require a disambiguating function call syntax for method and function invocation.
Edition Differences: Before the 2021 edition, during the search for visible methods, if the candidate receiver type is an array type, methods provided by the standard library
IntoIterator
trait are ignored.The edition used for this purpose is determined by the token representing the method name.
This special case may be removed in the future.
Warning: For trait objects, if there is an inherent method of the same name as a trait method, it will give a compiler error when trying to call the method in a method call expression. Instead, you can call the method using disambiguating function call syntax, in which case it calls the trait method, not the inherent method. There is no way to call the inherent method. Just don't define inherent methods on trait objects with the same name as a trait method and you'll be fine.