Consider the following code:
let x = String::from("123");
let bx = Box::new(x);
let dbx = *bx;
println!("{}", dbx);
//println!("{}", bx); // error due to ownership move
Here x
is created, later boxed into bx
, and later bx
is somehow unboxed in expression *bx
.
As far as I understand, ownership of data is moved from x
to bx
to dbx
, and data itself (at least memory occupied by struct String
) is copied from stack (x
) to heap (pointed by bx
) to stack (dbx
).
My main question is: how does compiler interpret *bx
?
It does not look like Deref for Box, since this would imply claiming ownersip of value, having reference to value (&String
).
It looks like compiler somehow desugars this to into_inner, but it is not obvious to me why.
So, What is going on here?
Bonus question: is there a way to see how code gets desugared, or how do I approach demystifying rust magic in general. Auto dereferencing involving various smart pointers is of particular interest to me now. I tried cargo-inspect but it does not seem to be helpful here.
Normally you are right that *
goes through Deref
or DerefMut
and only has access to &T
or &mut T
. However, Box
gets special treatment and is allowed by the compiler to move out of a dereference.
There is currently no trait that allows one to do that for user-defined types (a DerefMove
trait has been proposed but nothing accepted AFAIK). So there is no "desugaring" to be done; just *bx
is what you see and what is left is handled by the compiler internally. It really is "magic" in this case.
You can see that it doesn't desugar to into_inner
because the current implementation of that is to use *
(source):
pub fn into_inner(boxed: Self) -> T {
*boxed
}
See also: