Peeper

References and borrows

Borrowing with & and &mut, the rules the compiler checks, and returned references.

&x lends x to be read, and &mut x lends it to be changed. The rules follow Rust:

  • While a &mut is in use, nothing else uses x.
  • x must outlive every reference to it.

.* is the value a reference points to. A field is read through a reference directly.

fn shift(point: &mut Point, by: i32) {
    point.x = point.x + by;
    point.y = point.y + by;
}

Overlapping borrows

A &mut argument takes hold when the call starts. If a reference made before is still used after the call, the compiler shows all three places:

peeper check
[T0037]: `count` is borrowed as `&mut` while a reference to it is still in use  --> src/main.peep:8:12  | 6 |     let mut count = 1;7 |     let view = &count;  |                ------ reference made here8 |     update(&mut count, 2);  |            ^^^^^^^^^^ borrowed as `&mut` here9 |     println("count:", view.*);  |                       ------ reference used later here  |   = note: a `&mut` argument takes hold when the call startsCompilation failed with 1 error(s)

Returning a reference

A function can return a reference. in names the parameters it borrows from. Without in, the result borrows from every reference argument.

fn higher(score: &Score) -> &i32 in score {
examples/11_references.peep
// References: `&x` lends `x` to be read and `&mut x` to be changed, as in
// Rust: while a `&mut` is in use nothing else uses `x`, and `x` must outlive
// every reference to it. `.*` is the value a reference refers to; a field
// is read through a reference directly. A function may return a reference;
// `in` names the parameters it borrows from.
// Run: peeper run examples/11_references.peep
struct Point { x: i32, y: i32 }
struct Score { best: i32, last: i32 }

fn sum(point: &Point) -> i32 {
    return point.x + point.y;
}

fn shift(point: &mut Point, by: i32) {
    point.x = point.x + by;
    point.y = point.y + by;
}

fn double(n: &mut i32) {
    n.* = n.* * 2;
}

// The result points into `score` only.
fn higher(score: &Score) -> &i32 in score {
    if score.best > score.last {
        return &score.best;
    }
    return &score.last;
}

// Without `in`, the result borrows every reference argument.
fn better(a: &Score, b: &Score) -> &Score {
    if a.best > b.best {
        return a;
    }
    return b;
}

fn main() {
    let mut point = Point.{ x = 1, y = 2 };
    shift(&mut point, 10);
    println("shifted:", point, sum(&point));

    let mut n = 5;
    double(&mut n);
    println("doubled:", n);

    let lent = &mut point;
    shift(lent, 1);                 // a `&mut` passed to a call is lent again,
    shift(lent, 1);                 // so it is used twice
    println("through:", lent.x, lent.*);

    let alice = Score.{ best = 9, last = 4 };
    let bob = Score.{ best = 7, last = 8 };
    let high = higher(&bob);
    let best = better(&alice, &bob);
    println("higher:", high.*, "better:", best.best);
}
Output
shifted: Point { x: 11, y: 12 } 23
doubled: 10
through: 13 Point { x: 13, y: 14 }
higher: 8 better: 9

On this page