Skip to content

Instantly share code, notes, and snippets.

@jviereck
Last active May 2, 2016 15:21
Show Gist options
  • Save jviereck/449266ffda469bb17b66 to your computer and use it in GitHub Desktop.
Save jviereck/449266ffda469bb17b66 to your computer and use it in GitHub Desktop.

Rust Borrow Rules Overview

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

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.

Effect notation

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.

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: 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.

Rules for borrow of field reads and local references

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.

Rules for field reads:

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]

Rules for local variables reads:

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