A method call such as values.len() can borrow its receiver automatically.
It can also follow dereferences to find a method on another type. These
conveniences explain why many calls need no explicit & or *, but they
can also make two methods with the same name compete.
Follow the receiver types
For a call x.method(), lookup starts with the type of x and follows
its dereference chain. References have built-in dereferencing; types such
as Rc<T> provide it through Deref. An array-to-slice unsizing step can
extend the end of the chain.
For every type U in that chain, the search considers U, then &U, then
&mut U. This automatic borrowing is called autoref. The first applicable
receiver determines which part of the search can supply the method.
For x: Rc<RefCell<i32>>, the list is:
Rc<RefCell<i32>>
&Rc<RefCell<i32>>
&mut Rc<RefCell<i32>>
RefCell<i32>
&RefCell<i32>
&mut RefCell<i32>
There is no i32 step. RefCell<i32> does not implement Deref to its
contents. Its borrow() method returns a guard, Ref<'_, i32>, which
implements dereferencing while keeping the runtime borrow active.
At a particular candidate receiver, inherent methods take priority over visible trait methods. For generic parameters, methods supplied by trait bounds receive priority within the trait search. These priorities do not let a method skip ahead of an earlier receiver candidate.
Finding a method is not the end of type checking. The call can still fail because it needs a mutable borrow, would move a borrowed value, or requires an unsafe context. Rust does not simply keep searching for a different method when the selected call fails those checks.
Check your understanding
Why does let v = vec![1, 2, 3]; v.len() work without writing a reference?
Show answer
Hide answer
Vec::len takes &self. Method-call syntax can borrow v to supply that
receiver, so the call does not consume the vector. A method needing &mut self
instead requires mutable access; automatic borrowing cannot make an immutable
binding mutable.
For "hello".len(), the receiver already has type &str, which matches the
receiver of str::len. No by-value str call is needed.
A trait method can be found first
Here the trait's shared receiver is considered before the inherent method's mutable receiver:
struct Counter;
trait Inspect {
fn inspect(&self) -> &'static str;
}
impl Inspect for Counter {
fn inspect(&self) -> &'static str { "trait" }
}
impl Counter {
fn inspect(&mut self) -> &'static str { "inherent" }
}
fn main() {
let mut counter = Counter;
assert_eq!(counter.inspect(), "trait");
assert_eq!(Counter::inspect(&mut counter), "inherent");
}
The first call reaches &Counter before &mut Counter. “Inherent always
wins” would predict the wrong result. Naming the type in the second call
selects its inherent method directly.
A collision between two different borrow methods
RefCell::borrow checks the cell's runtime borrow state and returns a guard.
The Borrow trait instead provides a borrowed view of a value. Importing that
trait makes its methods available to lookup:
use std::borrow::Borrow;
use std::cell::RefCell;
use std::rc::Rc;
fn main() {
let x = Rc::new(RefCell::new(5));
let b = x.borrow(); // Error: the borrowed type needs an annotation.
}
At &Rc<RefCell<i32>>, trait lookup finds both Rc<T>: Borrow<T> and the
blanket implementation T: Borrow<T>. The result could therefore be either
&RefCell<i32> or &Rc<RefCell<i32>>. Without more type information, this
example produces E0283. Lookup has not reached the cell's inherent method.
An annotation can select the borrowed cell:
use std::borrow::Borrow;
use std::cell::RefCell;
use std::rc::Rc;
fn main() {
let x = Rc::new(RefCell::new(5));
let cell: &RefCell<i32> = x.borrow();
assert_eq!(*cell.borrow(), 5);
}
That is useful if you wanted a reference to the cell. It is different from obtaining a guard to the integer, and does not itself borrow the cell's contents at runtime.
Check your understanding
With Borrow in scope, how can you explicitly obtain a Ref<'_, i32> from x: Rc<RefCell<i32>>?
Show answer
Hide answer
RefCell::borrow(&x) names the inherent method and lets the argument coerce
from &Rc<RefCell<i32>> to &RefCell<i32>.
(*x).borrow() also works: its receiver is the cell, so its inherent method
wins at &RefCell<i32>. <RefCell<i32>>::borrow(&x) spells out the type
parameter in the first form. Choose the form that makes the intended method
easiest to recognize; RefCell::borrow(&x) is a clear choice here.
If the Borrow import is unnecessary, removing it also removes this collision.
Traits can be available through a prelude, a local definition or a generic
bound as well as a use, so “there is no import” does not mean “no trait methods
are available.” For example, a generic function with a T: module::Trait
bound can call that trait's methods without a separate import.
Disambiguate two trait methods
Two traits can offer the same method for the same receiver. When lookup cannot
choose, use <Type as Trait>::method to state which implementation you want:
struct Item;
trait Short { fn label(&self) -> &'static str; }
trait Long { fn label(&self) -> &'static str; }
impl Short for Item { fn label(&self) -> &'static str { "item" } }
impl Long for Item { fn label(&self) -> &'static str { "an item" } }
fn main() {
let item = Item;
// item.label() would be ambiguous (E0034).
assert_eq!(<Item as Short>::label(&item), "item");
assert_eq!(<Item as Long>::label(&item), "an item");
}
E0034 and the earlier E0283 describe different problems: competing methods versus an unresolved generic borrowed type. Read the candidates in the diagnostic before deciding whether to qualify the method or annotate a type.
Consider method lookup when designing a wrapper
Implementing Deref exposes target methods through your wrapper's lookup
chain. Wrapper methods, visible traits and future additions to the target can
affect which calls compile or which implementation they select.
Deref is appropriate when the wrapper is intended to behave transparently
like its target and dereferencing is cheap and unsurprising. It is a public
API commitment, not a general substitute for inheritance. An explicit accessor
may communicate the relationship better when that transparency is unwanted.
When a call surprises you, identify the receiver's exact type, list the candidate receiver forms, and check which traits are available. Then use a type-qualified or trait-qualified call to express the intended operation.