Thanks for your question! And its definitely a bit of an oddity that pops up in Move IR programs
I’m guessing that & and * on their own reference and dereference as would be expected in Rust/C
This is exactly correct. As such,
*& is borrow followed by a dereference (
ReadRef bytecode instructions).
In the Move bytecode, the only way to interact with the fields of a given struct is by reference. So in order to get a copy of a value in a struct, a programmer must first borrow a reference to the field (with &/
BorrowField), and then a copy of the value bound to that field can be created by using deref (with */
The Move IR is intended to be a thin, syntactic wrapper over the bytecode. As such, the mechanism for reading values out of a field is made explicit with
*& a borrow followed by a dereference.
In the grammar for the IR, dereference works over any expression. Similarly borrowing a field works over any expression. So you could borrow the field first, and then dereference it later. For example:
field_ref = &move(rsc).field;
field_value = *move(field_ref);
In this way,
*& is not a special operator, but the composition of two distinct operations.
- It can occasionally crop up in Rust, for dereference conversions. Is something similar happening here?
If you are familiar with Rust, think of
*&move(rsc).field as being roughly equivalent to
src.field.clone(). There is no implicit copying out of field references like there is in Rust (for values that implement the
Copy trait). So in the Move IR, a programmer must use the equivalent of
*&, anywhere that a copy of a field value is needed.
- It seems to be used with field accesses and move statements, as in
*&move(rsc).field . Is this some special syntax involved with either accessing fields (as in
& x . y is a single operation accessing a reference’s field) or with moving references where
&move(...) is the operation?
This might be more detail than you need, but let me unpack
- This is a composition of 3 separate instructions: A move of a local, a borrowing of a field, and a reading of a reference
- Each time a local is accessed, the value of that local can either be copied or moved. This must be specified in IR with
move(rsc) which correspond to the bytecode instructions of
copy(rsc) is used, a copy of the value stored in that local is put on the stack
move(rsc) is used, the value is moved out of the local and onto the stack. The local
rsc cannot be accessed again, until it is assigned a new value
- This borrows a field. As stated above, the only way to access fields of a struct is by reference
- The reference corresponding to
move(rsc) is “extended” to now be a reference accessing
- Concretely, the reference
move(rsc) is popped off the stack and the reference for
field is pushed on the stack
- This is a dereference. It “reads” the value from the reference
- When dereferencing, the reference is not modified, and a copy of the value in the reference is created
- Concretely, the reference
&move(rsc).field is popped off the stack, and a copy of the value at that location is pushed on the stack
- The bytecode instructions for this expression would be:
field would be represented as indexes in the bytecode)