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 in a closure

A local callback may need to inspect data that its caller will use again. Use & in a capture list to keep ownership outside the closure:

def main() -> Result[(), AllocError | IoError]:
    mut values = [4]?
    read = def [&values]() -> i64:
        values[0]
    print(read())?
    moved = read
    print(moved())?
    values.append(5)?
    print(values)?
    Ok(())
4
4
[4, 5]

The borrow follows the closure into moved and ends after its last call. Changing the list before a later call would conflict with that borrow. Borrowing closures cannot be returned from their creating function or stored in collections or sum types. Pass them to other functions through reference parameters:

def invoke(f: &Closure[[], i64]) -> i64:
    f()

def main() -> Result[(), IoError]:
    value = 7
    read = def [&value]() -> i64:
        value
    print(invoke(read))?
    Ok(())
7

The &Closure parameter borrows read implicitly; invoke(&read) is also valid. The capture remains borrowed throughout invoke, including while its other arguments are being evaluated. A closure can also explicitly borrow another closure; the same rule protects all of their captured owners.

Use &mut to modify an existing binding rather than moving it into private state:

def main() -> Result[(), IoError | OverflowError[i64]]:
    mut count = 0
    mut increment = def [&mut count]() -> Result[(), OverflowError[i64]]:
        count = count + 1
        Ok(())
    increment()?
    increment()?
    print(count)?
    Ok(())
2

Both the source and closure bindings need mut. The original count becomes available again after the last closure call. Reading it before another call would conflict with the exclusive capture.