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
``
is expressed in the short notation `&mut T.& S`, which, after dropping the
struct types becomes 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`.
// Describes the effect on the object/borrow the operation is taken on. In this // case, the &mut on the field is after the operation marked as mutable borrowed effect: &mut.[&mut @ MutB]
## Note on applying the borrow operations like `&mut` and `&`
Ending up with the correct / expected type for the l-value is more tricky
than it might seem at first place: 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:
```rust
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:
let a = reciever.field;
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.&mut --(@SomeOperation)--> &mut
Where here it is assumed, that reciever
has type &mut 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 &mut T.&mut S
→ &mut.&mut
.
&mut.&mut --(@MutB)--> &mut effect: &mut.[&mut @ MutB]
&mut.&mut --(@ImmB)--> & effect: &mut.[&mut @ ImmB]
&mut.& --(@MutB)--> Error cannot borrow immutable borrowed content as mutable
&mut.& --(@ImmB)--> & effect: &mut.[& @ ImmB]
&.&mut --(@MutB)--> Error
&.&mut --(@ImmB)--> & effect: &.[&mut @ ImmB]
&.& --(@MutB)--> Error
&.& --(@ImmB)--> & effect: &.[& @ ImmB]
&.&mut --(@MutB)--> Error
&.&mut --(@ImmB)--> & effect: &.[&mut @ ImmB]
&.& --(@MutB)--> Error
&.& --(@ImmB)--> & effect: &.[& @ ImmB]
mut T.S --(@MutB)--> &mut effect: mut T.[S @ MutB]
mut T.S --(@ImmB)--> & effect: mut T.[S @ ImmB]
T.S --(@MutB)--> Error: cannot borrow immutable field `a.b` as mutable
T.S --(@ImmB)--> & effect: &.[& @ ImmB]
&mut --(@MutB)--> &mut effect: [&mut @ MutB]
&mut --(@ImmB)--> & effect: [&mut @ ImmB]
& --(@MutB)--> Error cannot borrow immutable borrowed content as mutable
& --(@ImmB)--> & effect: [& @ ImmB]
T --(@MutB)--> Error cannot borrow immutable local variable `a` as mutable
T --(@ImmB)--> & effect: [T @ ImmB]