Thanks for the question!
At first glance, this code might seem dangerous, but it is actually safe. In the Move bytecode, you can have multiple mutable references to a given location, but only one of those references can be used at any point in time.
In your example, c
cannot be modified nor read from – only b
can be used. Once b
is released c
can be used.
For example
// VALID
a = 1;
b = &mut a;
c = &mut a;
*move(b) = 2;
*move(c) = 2;
// INVALID
a = 1;
b = &mut a;
c = &mut a;
*move(c) = 2; // error `a` is being borrowed by `b`
*move(b) = 2;
In some sense, the Move bytecode verifier separates the creation of references from the usage of references. This allows for very nuanced programs, even though it might seem dangerous in some examples (but luckily it is not).
More in depth:
Internally to the bytecode verifier, borrow relationships are modeled as a graph.
In the example
a = 1;
b = &mut a;
c = &mut a;
We would say that the local a
is borrowed by the reference stored in c
which is borrowed by the reference stored in b
. Thus b
can be mutated as it is not being borrowed. But c
cannot be mutated as it is being borrowed by b
It might seem a bit quirky that c
is being borrowed by b
, when c
was assigned after b
. When a location is borrowed again, any existing borrows are transferred to the new reference.
So
a = 1;
b = &mut a;
The local a
is borrowed by the reference stored in b
c = &mut a;
After this line, the local a
is borrowed by the reference stored in c
. The existing borrows on a
are transferred to the reference stored in c
. So the reference stored in c
is being borrowed by the reference stored in b
.