3

Assume we already know that String can become &str after deref for

impl ops::Deref for String {
    type Target = str;

    #[inline]
    fn deref(&self) -> &str {
        unsafe { str::from_utf8_unchecked(&self.vec) }
    }
}
  • This code can work because there is a deref chain: &String --> String ---> &str.

    fn uppercase(s: &str) -> String {
        s.to_uppercase()
    }
    
    fn main() {
        let s = String::from("hello");
        assert_eq!(uppercase(&s), "HELLO");
    }
    
  • Why does the code below not work even though there is a deref chain String ---> &str?

    fn uppercase(s: &str) -> String {
        s.to_uppercase()
    }
    
    fn main() {
        let s = String::from("hello");
        assert_eq!(uppercase(s), "HELLO");
    }
    
  • Why does the code below not work even though there is a deref chain: &String --> String?

    fn uppercase(s: String) -> String {
        s.to_uppercase()
    }
    
    fn main() {
        let s = String::from("hello");
        assert_eq!(uppercase(&s), "HELLO");
    }
    

Reference

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
gaoxinge
  • 459
  • 2
  • 8
  • 21
  • From the word dereference alone, then being able to dereference _requires_ a reference, which you don't provide in "code 2". I'm not sure what you mean in "code 3", you can't `deref()` a `&String` into a `String`. – vallentin Nov 05 '21 at 12:37

1 Answers1

3

From The Rust Programming Language:

Deref coercion is a convenience that Rust performs on arguments to functions and methods. Deref coercion works only on types that implement the Deref trait. Deref coercion converts such a type into a reference to another type. For example, deref coercion can convert &String to &str because String implements the Deref trait such that it returns &str. Deref coercion happens automatically when we pass a reference to a particular type’s value as an argument to a function or method that doesn’t match the parameter type in the function or method definition. A sequence of calls to the deref method converts the type we provided into the type the parameter needs.

Deref coercion converts references into references. It will auto-deref &value to &*value, &**value, &***value, etc., in order to convert one reference type into another that fits a parameter signature. The starting and ending types are always references.

Notably, it doesn't put the deref on the front. It doesn't do &value to *&value to **&value, which is why it won't convert &String to String or String to &str.

  • Turning &String into String would turn it into a move. If I wrote func(&s) and func takes a String it would be confusing if that compiled and actually moved s.

  • Similarly, turning String into &str would turn a move into a pass-by-reference. If I wrote func(s) I'd expect s to be moved, with ownership transferred to func. It should not compile if func takes a &str and I pass a String.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
  • In this [article](https://stackoverflow.com/questions/58023943/why-does-string-implicitly-convert-to-str-in-rust), one can transfer `String` to `&str` in receiver type by deref. So auto deref rule of receiver in method is different from argument in function? – gaoxinge Nov 05 '21 at 13:00
  • @gaoxinge Yes, [determining the type of the receiver is a completely different mechanism](https://stackoverflow.com/a/53344847). – Sven Marnach Nov 05 '21 at 13:23
  • Oh, shoot, I... somehow missed that was a quote? I must have been sleepy. Good point. I'll take another stab at it later I guess – trent Nov 08 '21 at 15:03