Reference, Box and Deref

Hello it's me again, the guy who can't make sense of references... And Deref...

Well, I thought I finally had it and why you can't do this:

let s = String::from("Hello");
let rs: &String = &s;
let s2: String = *rs; // (1) error: can't move out of *rs which is behind a shared reference

But then I reached the book's chapter about smart pointer and the Deref trait and realized you can do this:

let b = Box::new(String::from("Hello"));
let s: String = *b; // (2) What?!

I understand that a Box<String> owns its String while a &String does not. But then, isn't line (2) above de-sugared to:

let s: String = *(Deref::deref(&b));

And

Deref::deref(&b)

evaluate to a &String (sure, one that points to the Box owned string, but a &String still) ?

So then the question is: how come we can not move out of a direct reference to a String (like in (1)) whereas we can move out of the reference returned by the Box's Deref implementation (like in (2)) ?

Even more confusing (to me) is that after (2), the Box has been moved (or consumed): you can't use it.
But !!!

let b = Box::new(String::new("Hello"));
let rs: &String = &*b; // (3)
println!("{}", b); // Works fine !! Why ?!!!

It looks like in (3) the & operator see through the Box to get a reference to the Box owned String (without moving it out)...

It just beats me...

Thank for your help and understanding

Regards

Brieuc

Deref never allows move operations. Box allows it because it's special, and the * operator on box isn't actually using the Deref trait. No other type supports it.

3 Likes

Besides Box being special, it's also important to understand that expressions can evaluate to places, not values. The expression *b is not evaluated to a value (the String, being moved) if it's used e. g. in the expression &*b. If you think about it, this is similar to how an expression &x doesn't first evaluate the expression x to a value (moving out of x) but instead takes a reference directly to the existing value in its existing place in the variable x.

For non-special types using the traits Deref and DerefMut, the desugaring also involves thinking about places and how they're accessed. Using the place in a mutable way, e. g. for a type Foo: DerefMut<Target = T>

let x: Foo<T> = â€Ļ;

with an expression like &mut *x would desugar the sub-expression *x to *DerefMut::derefMut(&mut x),[1] whereas it it was used immutably, only the immutable desugaring you quoted above – *Deref::deref(&x) – would be used for *x.

Another example of a mutable usage – besides taking a mutable reference – would be assignment, e. g.

*x = new_t_value();

A more exhaustive overview over all place and value expressions and contexts can be found in the Rust Reference


  1. so that the whole expression &mut *x becomes &mut *DerefMut::deref_mut(&mut x); then the first &mut and * operating on the return value of deref_mut of type &mut T cancel each other out so the &mut x effectively comes to mean just DerefMut::deref_mut(&mut x) ↩ī¸Ž

7 Likes

& and * are inverses. Taking a reference to a place obtained by dereferencing a pointer-like type gives the original address. (In this case, the address of the heap buffer backing the Box.)

Oh ok. Do you have any links to documentation for that ?

https://doc.rust-lang.org/reference/special-types-and-traits.html#boxt

1 Like

Thanks.

Thanks for your elaborate answer. It helped a lot.

I have read the reference chapter you mentioned a while ago and again last night. But I still find it a bit confusing.

It mentions values and contexts but if the different types of values are clearly defined, contexts definitions are less clear to me.

Place expression contexts are defined ("The following contexts are place expression contexts..."). But when it comes to value contexts, they are only mentioned later in the "Moved and copied types" section: "When a place expression is evaluated in a value expression context..." leaving the non-expert users figure out what are these value expression contexts...

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.