Stuff the Identity Function Does (in Rust)
The identity function looks like this in Rust:
id
returns the same value that is passed in:
Beyond the obvious, it does some curious and fun things!
You can test this blog post’s code in the Rust Playground.
id Type Hints or Coerces
No magic, it’s just that you can specify with an explicit type which identity function you are calling. If the expression can coerce to that type, then it compiles.
id Forces References To Move
Let’s say we have a simple recursive datastructure:
And we want to walk it, with a mutable reference, through a loop.
Looks good? Rustc disagrees! (compile in the playground)
error: cannot borrow `current.next.0` as mutable more than once at a time [E0499] Some(ref mut inner) => current = inner, ^~~~~~~~~~~~~
It turns out Rust’s mutable references do something interesting, and most of
the time it’s very useful: when they are passed, they reborrow the local variable
rather than move it. The explicit equivalent of the reborrow would be &mut *current
.
id
tells a mutable reference to move instead of reborrow! This way it compiles:
This is a point where Rust could improve by learning to infer whether to
reborrow or move mutable references. Until then, we have id
.
id Makes Immutable Locals Mutable
id
returns just the same thing as you pass in. Except it’s now an rvalue, and
implicitly mutable.
This is no violation of Rust philosophy. Using mut
on locals is simple and
pragmatic, and mutability radiates from the owner. If your value is now
a temporary, it’s not owned by an immutable binding anymore (or any other
variable binding).
Rust has Dedicated Syntax for This!
If you thought that was cryptic, here’s one better. The secret syntax is just
{
and its companion }
, and it allows you to manipulate move semantics just
the same way id
does:
Force a Move Today
If you actually use this, I think moving
is actually a pretty good name
(move
is taken, it’s a keyword). Save the {}
for obfuscation contests.
Epilogue
It’s February 2017 and this is hilarious:
Calling id::<&T>(p)
, since it’s a passing a shared reference, inserts
a noalias
annotation for the pointer p
, which might otherwise not have been
there! As soon as llvm’s metadata propagation improves, it might even have
actual use.