Skip to content

Instantly share code, notes, and snippets.

@jviereck
Created March 26, 2015 00:03
Show Gist options
  • Save jviereck/6db68ba85b2fb62f3308 to your computer and use it in GitHub Desktop.
Save jviereck/6db68ba85b2fb62f3308 to your computer and use it in GitHub Desktop.

Rust Borrow Rules

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.

How to read the following:

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.

Rules for borrow of field reads and local references

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.

Rules for field reads:

&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]

Rules for local variables:

&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]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment