Does deref happen in method call with a immutable reference receiver but given mutable one

        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 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:

  1. T's inherent methods (methods implemented directly on T).
  2. Any of the methods provided by a visible trait implemented by T. If T is a type parameter, methods provided by trait bounds on T 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.

2 Likes