Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Borrow instead of transferring ownership

A shared reference, &T, permits observation. An exclusive reference, &mut T, permits mutation. The owner remains responsible for cleanup. Function signatures state which access is needed:

def first(values: &list[i64]) -> i64:
    values[0]

def add(values: &mut list[i64], value: i64) -> Result[(), AllocError]:
    values.append(value)?
    Ok(())

def main() -> Result[(), AllocError | IoError]:
    mut numbers = [1, 2]?
    print(first(numbers))?
    add(&mut numbers, 3)?
    print(numbers)?
    Ok(())
1
[1, 2, 3]

Collection operations such as append, indexing, and len work directly through references.

The parameter determines borrowing: first(numbers) implicitly lends numbers because first expects &list[i64]. Writing first(&numbers) also works. Use explicit &mut for mutable arguments, as in add(&mut numbers, 3). An owned parameter still takes ownership.

References can also be local bindings. &mut permits changing the target, not reassigning the reference binding. Use *reference to read or replace a scalar target:

def main() -> Result[(), IoError | OverflowError[i64]]:
    mut score = 10
    reference = &mut score
    *reference = *reference + 5
    print(*reference)?
    score = 20
    print(score)?
    Ok(())
15
20

The exclusive borrow ends after the last use of reference, so assigning to score afterward is allowed. A reference that will be used later keeps its loan live, including across branches and loop iterations:

def main() -> Result[(), AllocError | IoError]:
    mut numbers = [1, 2]?
    view = &numbers
    numbers.append(3)?
    print(view)?
    Ok(())
conflicting borrow: cannot modify or exclusively borrow `numbers` while it is borrowed

The diagnostic notes identify the borrow’s creation and the later use that keeps it live.

An exclusive reference can temporarily lend shared or exclusive access. Conflicting access through it is suspended until the child borrow’s last use. Passing an existing reference to a reference parameter reborrows it; the owner is not transferred.

When a conflict goes through a reference, the diagnostic names both the owner and the reference written at the failing use:

def main() -> Result[(), AllocError | IoError]:
    mut numbers = [1, 2]?
    reference = &mut numbers
    view = &reference
    reference.append(3)?
    print(view)?
    Ok(())
conflicting borrow: cannot modify or exclusively borrow `numbers` (through `reference`) while it is borrowed