Note: These are largely implemented, but freezing/lifetime enforcement is missing and they struggles across method calls.

Borrowed references are different from owning references: they neither know nor care which region owns the value they point to. As the name suggests, borrowed references are created to point to values that already exist in memory.

A very common scenario is when we have some existing value and, for some limited lexical scope, we want a reference able to work with that value. A borrowed reference is ideal for this requirement because:

Creating a Borrowed Reference

Here are several ways to borrow a reference to some existing value.

Borrowing from a variable

Place the ampersand operator in front of any variable's name to create a borrowed reference that points to the value held by that variable. This works equally well on any global, local or parameter variable:

imm glovar = 2
fn func(parm i32):
	mut localvar = 3
	imm ref1 = &glovar    // ref1's type: &i32
	imm ref2 = &parm      // ref2's type: &i32
	imm ref3 = &localvar  // ref3's type: &i32

Notice how the absence of a region specification alters the ampersand operator's behavior. Specifying a region allocates a new value in that region and returns an owning reference. Whereas, not specifying a region borrows a reference to some existing value, returning a borrowed reference. The absence of a specified region emphasizes how much a borrowed reference neither knows nor cares which region owns the value the references points to.

Borrowing from another reference

To explicitly create a borrowed reference out of some owning (or borrowed) reference, place the ampersand operator in front of a de-referenced reference:

imm allocref = &rc 1
imm borref = &*allocref    // Borrow from allocref

In most cases, however, borrowing a reference from an existing reference can be handled implicitly, as part of a function (or method) call or assignment. This happens when the receiver is given an owning reference but expects a borrowed reference. So long as the value type (and permission) match, the borrow is performed automatically.

// This function accepts a borrowed reference
fn incr(nbr &i32):
  *nbr + 1

fn main():
  imm allocref = &rc 1   // An owning reference pointing to an allocated value of 1
  incr(allocref)         // Coerces the rc reference to a borrowed reference

Because any reference may be safely coerced to a borrowed reference, functions and methods typically accept borrowed references as parameters. This capability is what makes possible the benefits mentioned earlier:

Referring to a field or array element

Unlike an owning reference, a borrowed reference may point to a value within some composite value, such as a field in a struct or an element in an array. Again, all we need to do is apply the ampersand operator:

imm ref1 = &apoint.x   // a field within a struct
imm ref2 = &vec[a]     // an element within an array

This kind of internal borrow is quite powerful, as it supports:


Like owning references, borrowed references may specify a permission which governs what may be done with the reference. This permission is specified after the ampersand operator. If none is specified, const is assumed.

mut a = 3
imm ref = &mut a   // Creates a mutable borrowed reference to a's value
*ref = 4           // a is now set to 4

Temporary Permission Transitions

The permission requested for the borrowed reference must be allowed by the source we are borrowing from. For example, a mutable borrowed reference may not be obtained from an immutable variable.

Using borrowed references does make it possible to temporarily transition a uni reference to some shared reference permission. During the lifetime of a borrowed reference, the source for the borrowed reference may not be used. However, once the lifetime of the borrowed reference expires, the original source becomes usable again:

imm uniref = &rc 5
  imm bref1 &mut i32 = uniref   // Coerce owning reference to a 'mut' borrowed reference
  imm bref2 = ref2              // Share with another borrowed reference
  *bref2 = *bref1 + 1
  *uniref = 6                   // Error, uniref is unusable during scope of borrowed 'bref1'
*uniref = 7                     // Allowed, since borrowed references have expired

Freezing access to the source of a borrow

When a borrowed reference is created, its source is always a named variable. That source variable either holds or points to the value we are borrowing a pointer to. When we borrow from the source, we are not just gaining access to the value, we are also gaining some or all of its access permissions.

Whenever we borrow from a source with uni permission, which guarantees only one live reference can exist to the value at a time, the source variable is made inaccessible for the lifetime of the borrowed reference in order to preserve that guarantee:

fn freeze(n i32):
  uni uniref = &one 3       // has permission uni
  if n > 1:
    imm borref = &*uniref   // borrowing makes uniref inaccessible in this block 
    imm m = *uniref         // oops! uniref is frozen and may not be accessed
  *uniref                   // Safe, because the borrow has expired.

Using a Borrowed Reference

Borrowed references may be dereferenced, compared, and handled in the same way as owning references.

Additionally, one may obtain borrowed references during each iteration or pattern matching by using a borrowed reference with the required permission as the source:

// increment every number in list
each x in &mut list:
  ++*x   // x is a mutable reference

Lifetime Constraints

The most distinguishing safety constraint on a borrowed reference is that its lifetime is constrained to never travel beyond the scope it is created in. Carefully managing the lifetime of a borrowed reference is needed for memory safety, as it prevents it from pointing to any value with a shorter lifetime.

This example demonstrates the importance of this restriction:

fn getval() i32:
  mut ref &i32
    imm a = 5
    ref = &a
  *ref   // Oops!

The scope of the variable a lasts only for the duration of the inner block. So when we are done executing that block, the variable a ceases to exist. However, by copying the borrowed reference into ref, we are effectively trying to extend its life beyond the scope it was created in and beyond the life of the value it points to. By attempting to dereference it, we are problematically trying to retrieve a value that is no longer there.

Most of the time, the compiler has all the information it needs to figure this out and generate a error. However, sometimes it needs help in the form of lifetime annotations.