NOTE: The rules shown here do not take the lifetime constraints into account. The rules could be easily adapted to cover the lifetimes as well but this is not done for similicty at this point.
The operations on the objects and fiels are denoted as follows:
@MutB: Take a mutable borrow
@ImmB: Take an immutable borrow
The types of an expression like reciever.field
, which might have the type
struct S {
id: isize
}
struct T {
field: &S;
}
let field = S { id: 0 };
let reciever = &mut T { field: &field };
reciever: &mut T
reciever.field: & S
are expressed in the short notation &mut T.& S
, which, after dropping the
struct types, become even shorter to &mut.&
. Note, that the denoted borrow types
are exactly the ones on the struct definitions - e.g. although a mutable borrowed
field on an immutable borrow reciever acts like an immutable borrow, it is still
denoted as &.&mut
and not as &.&
(in other words: the viewpoint adaption is
not taken into account).
A owning type like T
(in contast to an borrowed type like & T
) is expressed
in this short notation as just T
.
To capture the effect after applying a borrow operation on the reciever / reciever's field, the following notation is used:
&mut.[&mut @ MutB]
The brackets denote the parts the operation is active on. The effect type is
denoted by the & Effect
. In the case above, the field with type &mut S
on
the reciever of type &mut T
is marked as mutable borrowed (MutB
).
In this case, the &mut on the field is after the operation marked as mutable borrowed.
Ending up with the correct / expected type for the l-value is more tricky
than it might seem at first: Applying an &mut
mutable borrow operation
to an borrowed value (e.g. & T
) creates a double borrowed type &mut &T
by
default. If the l-value has an explicit type annotation, then rust will try to
convert the borrowed type.
Here are a few examples for demonstration purpose:
struct Entry<'a> {
id: &'a isize
}
fn main() {
let id = 0;
let entry = &mut Entry { id: &id };
// Implicit conversion.
//
// error: mismatched types:
// expected `&mut isize`,
// found `&isize`
let id_ref: &mut isize = entry.id;
// Force explicit conversion.
//
// error: cannot borrow immutable borrowed content as mutable
let id_ref: &mut isize = &mut entry.id;
// Apply the `&mut` operation without explicit types.
//
// In this case, the type of `id_ref` is `id_ref: &mut &isize`, which is often
// not the desired type.
let id_ref = &mut entry.id;
}
In the following, it was ensured, that the type l-value type on the target is of the actual correct type. This corresponds to the "Force explicit conversion" case in the last example.
Covering the case of:
// Field read.
let a = &reciever.field;
let a = &mut reciever.field;
// Local variable read.
let b = &reciever;
becomes in the shorthand notation:
reciever.field --(@SomeOperation)--> a
and, as for the borrow rules the types of the local variables/fields are the only necessary information, the last is converted in terms of the types:
&&.&mut --(@SomeOperation)--> &mut
Where here it is assumed, that reciever
has type & T
, field
is
defined on the struct of type T
as &mut S
and the type of a
is &mut S
.
Dropping the T
and S
type gives then eventually & T.&mut S
→ &.&mut
.
Reciever and field | Opp | Result | Effect \ Error message
--------------------+---------+--------+---------------------------------------
| | |
r: &mut T.f: &mut S | @MutB | &mut S | r.[f @ MutB]
r: &mut T.f: &mut S | @ImmB | & S | r.[f @ ImmB]
r: &mut T.f: & S | @MutB | error | cannot borrow immutable borrowed content as mutable
r: &mut T.f: & S | @ImmB | & S | r.[f @ ImmB]
| | |
r: & T.f: &mut S | @MutB | error | cannot borrow immutable field `r.f` as mutable
r: & T.f: &mut S | @ImmB | & S | r.[f @ ImmB]
r: & T.f: & S | @MutB | error | cannot borrow immutable borrowed content as mutable
r: & T.f: & S | @ImmB | & S | r.[f @ ImmB]
| | |
r: mut T.f: S | @MutB | &mut S | r.[f @ MutB]
r: mut T.f: S | @ImmB | & S | r.[f @ ImmB]
r: T.f: S | @MutB | error | cannot borrow immutable field `r.f` as mutable
r: T.f: S | @ImmB | & S | r.[f @ ImmB]
Reciever and field | Opp | Result | Effect \ Error message
------------------------------+-------------------------------------------------
| | |
r: &mut T | @MutB | &mut T | [r @ MutB]
r: &mut T | @ImmB | & T | [r @ ImmB]
| | |
r: & T | @MutB | error | cannot borrow immutable borrowed content as mutable
r: & T | @ImmB | & T | [r @ ImmB]
| | |
r: T | @MutB | error | cannot borrow immutable local variable `r` as mutable
r: T | @ImmB | & T | [r @ ImmB]