Exclusivity

At any moment a value may be borrowed by any number of shared references, &T, or by one exclusive reference, &var T — never both. While a &var borrow is in use, the value is reached only through it; while & borrows are in use, nothing changes the value under them. The compiler checks this for every reference, so a reader never sees a value change mid-read and two writers never overlap.

While the value is borrowed byReading the value directlyWriting it directlyAnother & borrowAnother &var borrow
one or more &Tallowederrorallowederror
one &var Terrorerrorerrorerror

The rule covers everything that borrows: an argument passed to a reference parameter, a method call through a self: &T or self: &var T receiver, and a local binding of reference type.

A borrow lasts until its last use

A borrow made for a call lasts for that call. A borrow bound to a local lasts from the binding to the last use of that local — not to the end of the block. Once the reference is used for the last time the value is free again:

struct Account {
    balance: int;
}

func Main() -> int {
    var alice = Account { balance: 10 };
    let wallet: &var Account = alice;
    wallet.balance += 5;       // last use of wallet: the borrow ends here
    alice.balance -= 1;        // fine: alice is no longer borrowed
    return 0;
}
flowchart LR
    b["let wallet: &var Account = alice"] --> u["wallet.balance += 5<br/>(last use)"]
    u --> f["alice is free again"]
    b -. "alice reachable only through wallet" .-> u

Moving the direct access before the borrow's last use is what makes the program wrong:

let wallet: &var Account = alice;
let before = alice.balance;    // error: wallet is still to be used
wallet.balance += 5;
error: cannot read 'alice.balance' while 'wallet' holds an exclusive borrow
  help: read through 'wallet' or wait until its last use

Read through the reference instead, or move the read after the reference's last use.

What overlaps

Two accesses conflict only when they reach the same storage. The compiler compares the places as written:

  • A whole value overlaps each of its fields and elements, and they overlap it.
  • Different fields of one struct do not overlap, so one can be borrowed exclusively while another is read or borrowed.
  • Elements overlap when their indexes are the same constant or the same expression: v[0] and v[0], v[i] and v[i].
struct Pair {
    x: int;
    y: int;
}

func Swap(a: &var int, b: &var int) {
    let saved: int = a;
    a = b;
    b = saved;
}

func Main() -> int {
    var pair = Pair { x: 1, y: 2 };
    Swap(pair.x, pair.y);          // two fields: no overlap
    var values = [1, 2, 3];
    Swap(values[0], values[2]);    // two different elements
    return 0;
}

The errors

Each conflict has its own message. All of them carry a note giving the line and column where the conflicting borrow began, and a help line naming the reference to use instead.

Reading a value while a &var borrow of it is in use.

error: cannot read 'alice.balance' while 'wallet' holds an exclusive borrow
  help: read through 'wallet' or wait until its last use

Writing a value while a borrow of it is in use. A shared borrow forbids writes because a reader expects the value to hold still; an exclusive one forbids every direct access:

error: cannot modify 'alice.balance' while it is immutably borrowed
error: cannot modify 'alice' while it is exclusively borrowed

Borrowing for writing while a reader is in use, including through a method with a self: &var T receiver:

error: cannot borrow exclusively 'alice' while it is immutably borrowed
  help: use 'view' for the access or wait until its last use

Borrowing at all while a writer is in use, including by calling a method with a self: &T receiver. Such a call is reported both as a read and as a borrow:

error: cannot read 'alice' while 'wallet' holds an exclusive borrow
  help: read through 'wallet' or wait until its last use
error: cannot borrow 'alice' while it is exclusively borrowed
  help: use 'wallet' for the access or wait until its last use

One value in two arguments of the same call, when at least one of them is &var. Two &T arguments may share a value; an exclusive one must have it to itself:

error: call arguments create overlapping exclusive borrows of 'alice'
  help: split the accesses into non-overlapping calls

The same message names a field or an element when that is what is passed twice: Swap(pair.x, pair.x) reports 'pair.x', and Swap(values[0], values[0]) reports 'values[0]'.

Outside the rule

Raw pointers are not borrows. Taking an address with @, holding several *var T to one value, and writing through them are all accepted; keeping such accesses apart is the program's responsibility. A slice is a view built on a pointer and is not tracked as a borrow either.

See also