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?
Deref coercion is a convenience that Rust performs on arguments to functions and methods. Deref coercion converts a reference to a type that implements Deref into a reference to a type that Deref can convert the original type into.
Rust will also insert automatic dereferencing as part of deref coercion. This is a special coercion that can only convert from one reference type to another.
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 built 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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With