17

I can't seem to figure out why:

let a = Box::new(5i32);
let _:() = *a;

tells me that the assigned type on the second line is i32 and not &i32 since Deref.deref() (which I assume is being called at *a), returns &T.

Also, if I were to call deref() myself:

let _:() = <Box<i32> as Deref>::deref(&a);

I get the expected &i32.

Mansour
  • 1,787
  • 2
  • 20
  • 33

3 Answers3

20

Dereferencing doesn't necessarily produce an (intermediate) value. Consider

let b = Box::new(1);
(*b).clone();

The method i32::clone() is called with a &self argument where the reference points to the value inside the box, not to a temporary value that could be produced by (*b).

The trait Deref is part of implementing dereferencing (just like DerefMut).

There is no corresponding trait to what * can additionally do on a box: Move the inner value out and discard the box; this is colloquially called DerefMove but remains a compiler-hardcoded box speciality at this point.

When the compiler sees (*a), it has to infer whether to use Deref, DerefMut or “DerefMove”; it is inferred from how the expression is used: if you call a &self method on the result, Deref is used, for example.

Edited: Inherently copyable types (trait Copy), use Deref followed by copy instead of “DerefMove”; this is then no longer resticted to Box, but works with all smart pointers.

bluss
  • 12,472
  • 1
  • 49
  • 48
  • Moving out isn't limited to `Box`; it also [applies to references](http://is.gd/Wgqq2v). Of course, you can't actually move out of a reference, but it tries. – Shepmaster Nov 11 '15 at 15:54
  • @bluss This helps a lot - but given my updated knowledge of derefing and a little testing, `*` doesn't discard the box after extracting the value out, as I expected. Since `Deref.deref()` as implemented on `Box` does a share borrow (ie `&self`), it leaves the original owner of the `Box` intact. Did I understand this correctly? – Mansour Nov 11 '15 at 16:10
  • @Shepmaster Well it has an error that says it's not possible, you mean :-) – bluss Nov 11 '15 at 16:39
  • @Mansour Yes, that's right, I forgot the case that Shepmaster explains and applies to your case -- Deref followed by Copy. An integer implements the `Copy` trait. – bluss Nov 11 '15 at 16:40
  • But as explained, the `(*a).clone()` case does not copy the integer even if it's a `Box`, you call with a reference pointing into the box. – bluss Nov 11 '15 at 16:41
18

*foo is not the same as calling foo.deref(). If it were, you'd never be able to actually dereference a value. ^_^

* is syntactic sugar for "call Deref::deref and then go into the address provided". This is just what people expect from that operator. Deref exists to make it extensible.

That last bit glosses over some details that bluss' answer covers much better.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
0

Box has an as_ref() implementation which simply returns a reference to the inner value:

let a = Box::new(5i32);
let _: &i32 = &a;
Kaplan
  • 2,572
  • 13
  • 14