15

After some discussion, I'm now a little bit confused about the relation between auto-dereferencing and deref coercion.

It seems that the term "auto-dereferencing" applies only when the target to dereference is a method receiver, whereas it seems that the term "deref coercion" applies to function arguments and all contexts it needs to.

I thought that a dereference does not always involve deref coercion, but I'm not sure: does dereferencing always use some Deref::deref trait implementation?

If so, is the implementor of T: Deref<Target = U> where T: &U built into the compiler?

Finally, it sounds natural to use the term "autoderef" in all the cases where the compiler implicitly transforms &&&&x to &x:

pub fn foo(_v: &str) -> bool {
    false
}

let x="hello world";
foo(&&&&x);

Is this the general consensus of the community?

Peter Hall
  • 36,534
  • 10
  • 79
  • 144
attdona
  • 12,023
  • 5
  • 36
  • 48

1 Answers1

17

The parallels between the two cases are rather superficial.

In a method call expression, the compiler first needs to determine which method to call. This decision is based on the type of the receiver. The compiler builds a list of candidate receiver types, which include all types obtained by repeatedly derefencing the receiver, but also &T and &mut T for all types T encountered. This is the reason why you can call a method receiving &mut self directly as x.foo() instead of having to write (&mut x).foo(). For each type in the candidate list, the compiler then looks up inherent methods and methods on visible traits. See the language reference for further details.

A deref coercion is rather different. It only occurs at a coercion site where the compiler exactly knows what type to expect. If the actual type encountered is different from the expected type, the compiler can use any coercion, including a deref coercion, to convert the actual type to the expected type. The list of possible coercions includes unsized coercions, pointer weakening and deref coercions. See the the chapter on coercions in the Nomicon for further details.

So these are really two quite different mechanisms – one for finding the right method, and one for converting types when it is already known what type exactly to expect. The first mechanism also automatically references the receiver, which can never happen in a coercion.

I thought that a dereference does not always involve deref coercion, but I'm not sure: does dereferencing always use some Deref::deref trait implementation?

Not every dereferencing is a deref coercion. If you write *x, you explicitly dereference x. A deref coercion in contrast is performed implicitly by the compiler, and only in places where the compiler knows the expected type.

The semantics of dereferencing depend on whether the type of x is a pointer type, i.e. a reference or a raw pointer, or not. For pointer types, *x denotes the object x points to, while for other types *x is equivalent to *Deref::deref(&x) (or the mutable anlogue of this).

If so, is the implementor of T: Deref<Target = U> where T: &U built into the compiler?

I'm not quite sure what your syntax is supposed to mean – it's certainly not valid Rust syntax – but I guess you are asking whether derefencing an instance of &T to T is built into the compiler. As mentioned above, dereferencing of pointer types, including references, is build into the compiler, but there is also a blanket implementation of Deref for &T in the standard library. This blanket implementation is useful for generic code – the trait bound T: Deref<Target = U> otherwise wouldn't allow for T = &U.

Sven Marnach
  • 483,142
  • 107
  • 864
  • 776
  • I think the point is in the last sentence. Rephrasing my question: the semantic of dereferencing known by the compiler may be called auto-dereferencing? At coercion site there is more then a `deref coercion` mechanisms, because, as you pointed out, the defer blanket impl `fn deref(&self) -> &T { *self }` is idempotent and it does not explain &&&&x -> &x. – attdona Nov 17 '18 at 10:59
  • @attdona I can't follow. Auto-dereferencing means implicit rather than explicit dereferencing. It has nothing to do with the semantics of dereferencing. I also can't make sense of the word "idempotent" in this context. A function can only be idempotent when the range is a subset of the domain, which isn't the case here - the function maps values of type `&&T` to values of type `&T`. – Sven Marnach Nov 17 '18 at 21:35
  • I used the term idempotent inappropriately to signify that the deref blanket implementation transforms a &T in a &T. Clearly if we apply `deref()` to a `&&T`, or a `&&&T` we got a `&T`, but in this case the main actor I see in action is auto-deref. After the auto-deref of the `&&&x.deref()` receiver, `deref()` returns a `&T` from a `&T`: a sort of do-nothing operation (I assume `deref` it is called because the docs states that `deref` is always called but to me it seems useless in this case). Which term best describe what is happening: auto-deref or deref coercion? – attdona Nov 18 '18 at 15:34
  • @attdona I'm sorry, I still can't follow. The prototype of the blanket deref implementation is essentially `deref(&&T) -> &T`, while you appear to think it's `deref(&T) -> &T`. I may have misread your comment though. – Sven Marnach Nov 18 '18 at 15:45
  • Many Thanks for your patience!. I wrongly thought blanket impl as `deref(&T) -> &T` and this make me write non-sense comments. – attdona Nov 18 '18 at 18:23