Skip to content

Rust Borrowing

On the ownership page you saw a problem: passing a String to a function moves it, so you can’t use it afterward. Constantly moving values around (or cloning them) would be painful. Borrowing is Rust’s solution.

Borrowing means letting some code use a value without taking ownership of it — like lending a friend a book. They can read it, then give it back, and you still own it.

You create a reference by putting & in front of a value. A reference lets a function look at the data without owning it:

fn print_length(s: &String) { // borrows s, doesn't own it
println!("Length: {}", s.len());
}
fn main() {
let text = String::from("hello");
print_length(&text); // lend text to the function
println!("Still mine: {text}"); // ✅ text is still valid here
}

Output:

Length: 5
Still mine: hello
▶ Try it Yourself

The &text says “let this function borrow text.” Because the function only borrowed it, ownership stays with main, and you can keep using text afterward. Compare this to the ownership page, where the same code without & caused an error.

A plain & reference is read-only — you can look but not touch. If a function needs to change the borrowed value, use a mutable reference with &mut:

fn add_excitement(s: &mut String) {
s.push_str("!!!");
}
fn main() {
let mut text = String::from("hello");
add_excitement(&mut text); // lend it mutably
println!("{text}"); // hello!!!
}

Output:

hello!!!
▶ Try it Yourself

Notice three things had to line up: text is declared mut, the function asks for &mut String, and the call passes &mut text. All three are required to change a borrowed value.

Rust enforces two rules about references. They exist to prevent data races — a whole class of bugs where two pieces of code touch the same data at the same time and corrupt it.

At any given time, you can have either:

  • any number of read-only references (&), or
  • exactly one mutable reference (&mut),

but not both at once.

In plain English: many readers or one writer, never both together. This code breaks the rule:

fn main() {
let mut text = String::from("hello");
let r1 = &text; // a read-only borrow
let r2 = &mut text; // ❌ can't borrow mutably while r1 exists
println!("{r1} {r2}");
}

The compiler stops you:

error[E0502]: cannot borrow `text` as mutable because it is also borrowed as immutable

These rules are checked entirely at compile time, so they cost nothing when your program runs. This is how Rust can promise “no data races” and still be as fast as C — the safety is proven before the program ever starts.

  • Borrowing (&) lets code use a value without taking ownership — you get it back.
  • A plain & reference is read-only; use &mut to modify the borrowed value.
  • To mutate through a reference, the variable, the parameter, and the call all need mut/&mut.
  • The rule: many readers or one writer, never both at once.
  • All of this is checked at compile time, so it’s free at runtime.

Quick check

1. What does putting `&` in front of a value do when passing it to a function?

2. According to the borrowing rules, which combination is allowed at the same time?

3. What do you need to modify a value through a reference?

4. Why can Rust enforce the borrowing rules without any runtime performance cost?

Score: 0 / 4