Docs/Memory

Borrows & access

Read-only and writable views of data.

On this page

A borrow, written with &, gives a function access to existing data without copying it. A borrow only reads unless its type says mut.

Borrow types#

Type Meaning
&[T] read-only view of elements
mut &[T] view whose elements may be written
&T read-only borrow of one value
mut &T borrow of one value that may be written
local ... a borrow the function only uses during the call

The & operator makes the borrow: &value is as writable as value itself.

Read a sequence#

Any array, slice, fixed array or string converts to &[T], so one function accepts them all:

Valk
fn total(values: local &[int]) int {
    let sum = 0
    each values as value : sum += value
    return sum
}

fn count_spaces(text: local &[u8]) uint {
    let count: uint = 0
    each text as byte {
        if byte == ' ' : count++
    }
    return count
}

fn main() {
    let fixed: [int x 2] = { 4, 5 }
    println(total(Array[int]{ 1, 2, 3 })) // 6
    println(total(&fixed))                 // 9
    println(total(.{ 7, 8 }))              // 15
    println(count_spaces("a b c"))         // 2
}

Writing through a read-only borrow, or calling a method that changes it, is a compile error.

Write through a borrow#

mut lets the function change the caller's elements:

Valk
fn reset(values: local mut &[int]) {
    each values as _, index : values[index] = 0
}

fn main() {
    let scores = Array[int]{ 10, 20, 30 }
    reset(scores)
    println(scores[0]) // 0
}

The mut in the signature tells callers that their data may change. A String can never be passed as mut.

Borrow one value#

&T and mut &T work for a single value, such as a struct you want to change in place:

Valk
struct Point {
    x: int
    y: int
}

fn move_right(point: local mut &Point, by: int) {
    point.x += by
}

fn main() {
    let points = Array[Point]{ .{ x: 1, y: 1 } }
    move_right(&points[0], 5)
    println(points[0].x) // 6

    let p = Point { x: 0, y: 0 }
    move_right(&p, 2)
    println(p.x) // 2
}

Local borrows#

A parameter marked local promises not to keep the borrow after the call. Only such a parameter accepts storage of the current function, like a fixed array or a .{ ... } literal:

Valk
fn first(data: local &[u8]) u8 {
    return data[0]
}

fn main() {
    let key: [u8 x 4] = { 9, 8, 7, 6 }
    println(first(&key))       // 9
    println(first(.{ 1, 2 }))  // 1
}

A parameter without local may store what it gets, so it only takes heap data such as an Array or a class property. The compiler tells you when a parameter could be local. Most standard library functions that take bytes, like io.Writer.write, take local borrows.

Return part of a borrow#

A function can return part of what it was given with a local result:

Valk
fn tail(data: local &[u8]) local &[u8] {
    return data.view(1, data.length - 1)
}

fn main() {
    let bytes: [u8 x 3] = { 1, 2, 3 }
    let rest = tail(&bytes)
    println(rest.length) // 2
    println(rest[0])     // 2
}

When heap data goes in, the result is an ordinary borrow that may be stored.