🎓 A1Learner
🔍

Rust Borrowing

Borrowing is a way to look at a value without taking ownership of it. The borrowed look is a reference, written with &.

Borrow with &

Put & in front of a value to lend it. The owner keeps the value, and the borrow is only a look at it:

let text = String::from("rust");
let look = &text;

A function can borrow instead of taking

The parameter &String borrows the text instead of taking it, so the caller keeps it:

fn word_count(s: &String) -> usize { s.split_whitespace().count() }
let text = String::from("rust is fast");
println!("{} words, text kept: {}", word_count(&text), text);

Pass by value: a Copy type stays with the caller

An array of numbers is Copy, so the function works on its own copy and the caller's array is untouched:

fn zero(mut arr: [i32; 3]) -> [i32; 3] {
    arr[0] = 0;
    arr
}
let arr = [10, 20, 30];
let changed = zero(arr);
println!("caller {:?} copy {:?}", arr, changed);

Pass by reference: changes reach the caller

fn zero_first(arr: &mut [i32; 3]) {
    arr[0] = 0;
}
let mut arr = [10, 20, 30];
zero_first(&mut arr);
println!("caller {:?}", arr);

Only a mut owner can lend &mut

Ask for &mut of a value that was not declared mut, and the compiler refuses:

let s = String::from("hi");
let r = &mut s; // error E0596: cannot borrow `s` as mutable, as it is not declared as mutable

A live borrow blocks a push

look borrows the whole text. While look will still be used, push may not change the text it points at:

let mut s = String::from("hi");
let look = &s;
s.push('!'); // error E0502: cannot borrow `s` as mutable because it is also borrowed as immutable
println!("{look}");

Example

fn word_count(s: &String) -> usize { s.split_whitespace().count() }
fn main() {
    let text = String::from("rust is fast and safe");
    let n = word_count(&text);
    println!("'{}' has {} words", text, n);
}

Key points

  • &T is a shared reference: use it to read the value
  • Use *ref to follow a reference back to the value, called dereferencing
  • The value must live at least as long as the reference to it — Rust checks this
  • A reference in safe Rust is never empty — there is no null reference
  • While a reference to a value will still be used, that value cannot be destroyed.
  • Giving it away must wait.
  • A function that takes ownership waits until the borrow's last use.

In this example, text owns the String "rust is fast and safe". word_count takes &String, so the call word_count(&text) lends the text instead of giving it away. Inside the function, split_whitespace breaks the text into words and count() gives 5. Back in main, text is still owned by main, so the println! can print it with the word count. No copy was made anywhere.

Note

In the next lesson you will meet &mut, the borrow that allows changes.

Many read-only borrows can exist at once.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

Why pass &String to a read-only function instead of String?