Why do I need to use `&` to trigger deref coercion in rust?

122 views Asked by At

From The Rust Programming Language book:

Deref coercion converts a reference to a type that implements the Deref trait into reference to another type

Questions in code snippet below:

  • Why & is required for deref to be implicitly called, isn't m already a reference?

  • hello(m.deref().deref()) works without & operator MyBox<String> -> String -> &str convert is happening underneath:

    • if I add & in front of convert sequence - that would be &MyBox<String> -> &String -> &&str, why && is folded in final case (e.g. &&str -> &str when hello(&m) is called)?
  • Why I cannot use hello(m) instead, without & for passing m?

use std::ops::Deref;

struct MyBox<T>(T);

impl<T> MyBox<T> {
    fn new(x: T) -> MyBox<T> {
        MyBox(x)
    }
}

impl<T> Deref for MyBox<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

fn hello(name: &str) {
    println!("Hello, {name}!");
}

fn main() {
    let m = MyBox::new(String::from("Rust"));

    // Why `&` is required for deref to be implicitly called, 
    // isn't  m already a reference?
    hello(&m);

    // code below works without & operator 
    // MyBox<String> -> String -> &str convert is happening here
    hello(m.deref().deref());

    // why I cannot use the code like one below, 
    // without & for passing m?, e.g. 
    // hello(m);
}

Explanation by compiler for hello(m):

   Compiling 
error[E0308]: mismatched types
  --> src/bin/tmp3.rs:38:11
   |
38 |     hello(m);
   |     ----- ^ expected `&str`, found `MyBox<String>`
   |     |
   |     arguments to this function are incorrect
   |
   = note: expected reference `&str`
                 found struct `MyBox<String>`
note: function defined here
  --> src/bin/tmp3.rs:19:4
   |
19 | fn hello(name: &str) {
   |    ^^^^^ ----------
help: consider borrowing here
   |
38 |     hello(&m);
   |           +

For more information about this error, try `rustc --explain E0308`.
error: could not compile `luhn` (bin "tmp3") due to previous error

Isn't m already borrowed when passed in hello(m)?

1

There are 1 answers

0
cafce25 On

You've got the first chain types wrong it's not MyBox<String> -> String -> &str but MyBox<String> -> &String -> &str, that works because calling a method automatically inserts the necesary reference.

But from the list of coercion types you can see only:

  • &T or &mut T to &U if T implements Deref<Target = U>

is supported, T to &U where T implements Deref<U> or T to &T are simply not amongst the valid coercion types.

So in conclusion the compiler never coerces an owned value to a reference, but coercion from references to other ones is done automatically when the right traits are implemented.

The only time the compiler will insert borrows is when using the method syntax on the self parameter, anywhere else only coercion can happen.