Type coercion for Arc

This interpretation seems a bit inaccurate as well IMO. Really, the thing here is that the syntax *x transforms place expression into place expression while Deref::deref transforms reference into reference. Both things do actually dereference one level — the extra * in *Deref::deref(&x) is balanced out by the extra &, *x goes from T to U while the underlying Deref::deref call goes from &T to &U.

But place expressions are a somewhat weird concept when you first think about them more deeply. When you do things like assigning let y = x, then the RHS expression x is evaluated to a value, possibly with effects like moving that value out of x, whereas in other contexts e. g. x += 1 the expression on the left hand side only refers to the place x.

A x += 1 operation corresponds to a call add_assign(&mut x, 1) suddenly for taking about the place of x behind a function abstraction, we need to introduce a mutable reference. Really, with deref it's more ore less just the same.

The connection to deref coercion works IMO better conceptually if you think about it the other way: Instead of considering *x to mean *coerce(&x) (i. e., take reference, then coerce, then remove reference), it might be better to consider * the more primitive operation and to think of the coercion of some y: &T into &U as the compiler sparing you the need to write out the expression &**y.

  • In the expression &**y, the &T turns into *y: T, into **y: U and then into &**y: &U. Only the middle * converting T to U is not some built-in deref operation for a reference, so it desugars to the *Deref::deref(&…) expansion, giving &**y the desugaring &*Deref::deref(&*y). Finally, we can simplify by removing redundant &* combinations, and see how &**y is essentially the same as Deref::deref(y).

One good reason why “considering *x to mean *coerce(&x)” might be suboptimal is because coercion works transitively, you can coerce &T into &V when T: Deref<Target = U> and U: Deref<Target = V>, whereas an expression *x always only does a single level of Deref::deref call; you'll need **x to turn T into V.

  • For **x, the desugaring *Deref::deref(&*Deref::deref(&x)) can again be reduced (by removing redundant &*) to just *Deref::deref(Deref:deref(&x)). Similarly, ***x would be *Deref::deref(Deref::deref(Deref:deref(&x))). This neatly shows how the change from “reasoning with place expressions” to “working with references”, and back, respectively only needs to happen once in such a chain.
3 Likes