References

A reference is a value that represents a place.

References can be created using the & reference operator.

let x = 1; // x: N32
let r = &x; // r: &N32

r is now a reference to the place that x evaluated to. It is of type &N32, the type representing references to N32s.

References can be unwrapped using the & reference pattern.

let x = 1;
let r = &x;

let &y = r; // unwrap the reference `r` into a place, and name it `y`
y = 2; // write the value `2` into the place

x // 2

References can be passed to function calls to allow mutation of local variables:

fn increment(&n: &N32) { 
  n += 1;
}

let x = 0;
increment(&x);
x // 1

Under the hood, methods that mutate their receiver use references:

let x = [1, 2, 3];
x.push_back(4); // equivalent to: `List::push_back(&x, 4)` (note the reference!)
x // [1, 2, 3, 4]

References are also useful when a function only needs to access part of a data structure:

// Inefficient

fn is_empty(list: List[N32]) -> N32 {
  list.len() != 0
}

let x = [1, 2, 3, 4];
let e = is_empty(x); // inefficient; clones the whole list
x // [1, 2, 3, 4]
e // false
// Efficient

fn is_empty(&list: &List[N32]) -> N32 {
  list.len() != 0
}

let x = [1, 2, 3, 4];
let e = is_empty(&x); // efficient; no clones necessary
x // [1, 2, 3, 4]
e // false

(This is why List::len takes its receiver by reference, despite not needing to mutate it.)

Dereference Operator

The place contained in a reference can also be accessed with the * dereference operator:

let x = 1;
let r = &x;
*r = 2;
x // 2

(Note that there are currently some bugs/limitations with this; if you have a reference stored in a variable, it is preferred to use reference patterns instead.)

This can be useful when a function returns a reference:

let x = [1, 2, 3];
*x.get(2) = 4;
x // [1, 2, 4]

The * operator can also be written as a postfix operator using .*:

let x = [1, 2, 3];
x.get(2).* = 4;
x // [1, 2, 4]

Dereference Pattern

The * dereference pattern takes a value pattern of type &T, and yields a place pattern of type T. This can be used, for example, to "split" a reference to a struct into references to its fields.

struct Pair(N32, N32);

fn increment(&n: &N32) { 
  n += 1;
}

fn increment_pair(r: &Pair) {
  let &Pair(*x, *y) = r; // x: &N32, y: &N32
  increment(x);
  increment(y);
}

let pair = Pair(0, 0);
increment_pair(&pair);
pair // Pair(1, 1)