struct S {
s: String,
}
impl S {
fn call_immutable(&self) {
println!("immutable call");
}
}
let mut v = S {s: String::from("temp")};
let mut_ref = &mut v;
// call_immutable method only take an immutable reference receiver.
// Rust references: https://doc.rust-lang.org/reference/type-coercions.html#coercion-sites
// > For method calls, the receiver (self parameter) can only take advantage of unsized coercions.
// So, &mut self -> &self type coercions cannot be happen
// considerating that https://doc.rust-lang.org/reference/expressions/method-call-expr.html
// mut_ref.call_immutable() -> (Deref::deref(&mut_ref)).call_immutable()
// Does Deref::deref(&mut_ref) deref happen, since impl Deref<Target = T> for &mut T
mut_ref.call_immutable();
Method calls don't have to operate according to the regular coercion rules. Instead, they use a special lookup procedure:
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 afterT
.For instance, if the receiver has type
Box<[i32;2]>
, then the candidate types will beBox<[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.
In this case, we're looking up a method call_immutable()
on the receiver type &mut S
. So we have candidate types &mut S
, &&mut S
, &mut &mut S
, S
(by dereferencing), &S
, and again &mut S
. The only visible call_immutable()
method is S::call_immutable(&S)
, so &S
the point where the lookup ends.
Therefore, the function is called as if you wrote S::call_immutable(&*mut_ref)
. However, the *
in &*mut_ref
never invokes any Deref
or DerefMut
implementation: for pointer types like &T
and &mut T
, dereferencing is a primitive operation defined by the language. (Otherwise, you'd end up with an infinite loop trying to expand something like (*mut_ref).s
.) Here, the effect is to create a new immutable reference that points to *mut_ref
, causing a reborrow of mut_ref
.
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.