9.1References: borrowing a value

Last updated June 12, 2026

Chapter 8 closed with a promise: every awkward moment in it (the tuple dance, the defensive clone, the function that swallowed your String) dissolves into a single character. Time to collect. Here's the give-and-take-back dance from lesson 8.6 one last time, a function that needs to hand its argument back because using it consumed it:

fn measure(text: String) -> (String, usize) {
    let len = text.len();
    (text, len)
}

fn main() {
    let message = String::from("hello");
    let (message, len) = measure(message);
    println!("{message} is {len} bytes long");
}
hello is 5 bytes long

It works, and it's miserable. measure wants to look at a String, but the only tool chapter 8 gave us hands over the whole value, so the function must mail it back in a tuple, and the caller must unpack it. Asking someone the time shouldn't require giving them your watch.

The & fix

Here's the same program the way a Rust programmer would actually write it:

fn measure(text: &String) -> usize {
    text.len()
}

fn main() {
    let message = String::from("hello");
    let len = measure(&message);
    println!("{message} is {len} bytes long");
}
hello is 5 bytes long

No tuple, no dance, and main keeps using message after the call as if nothing happened. Because nothing did happen: message never stopped being the owner.

Lesson 8.6 named the words in passing; this is where they live. The &message at the call site creates a reference: a value that refers to another value without owning it. Creating a reference to a value is called borrowing the value, and the vocabulary is well chosen. When you borrow a book, the book still belongs to its owner; you're expected to look at it, not burn it, and eventually you give it back. No part of that transaction transfers ownership.

Read the two ampersands aloud and the signature becomes a sentence. At the call site, &message says "lend measure a view of message." In the signature, text: &String says "I accept a borrowed String; I will not be keeping it." They're two halves of one agreement, written down on both sides of the call, which is exactly where you want an agreement written.

If the word view is ringing a bell, it should. Lesson 5.4 introduced &str as a viewer watching text through a window while a String owns it. That wasn't a special trick for strings. It was your first reference, and as of today every & in the language is one: a cheap, non-owning window onto a value that someone else keeps alive.

What a reference looks like in memory

Lesson 8.2 gave us the picture for a String: a small fixed-size handle in the stack frame (address of the heap block, length, capacity), with the actual text in the block. A reference adds one more small box to the picture: it holds the address of the handle. Following the trail from text to message to the heap is the compiler's problem, not yours; the part that matters is what the reference doesn't hold, which is ownership of anything.

That answers the question chapter 8 trained you to ask: who cleans up? Inside measure, the parameter text goes out of scope at the closing brace, and... nothing happens to the String. Dropping a reference frees nothing, because the reference owns nothing. The watcher leaving the window doesn't bulldoze the bicycle. message is dropped exactly once, at the end of main, by its one and only owner, and the three rules of ownership from lesson 8.1 come through without a scratch.

References are immutable by default

Borrowing a book comes with an unspoken rule: no writing in the margins. References have the same rule, and it's enforced. A plain & reference lets you read the value, full stop. Watch what happens when a function tries to go further:

fn add_world(text: &String) {
    text.push_str(", world");
}

fn main() {
    let message = String::from("hello");
    add_world(&message);
}
error[E0596]: cannot borrow `*text` as mutable, as it is behind a `&` reference
 --> src/main.rs:2:5
  |
2 |     text.push_str(", world");
  |     ^^^^ `text` is a `&` reference, so it cannot be borrowed as mutable
  |
help: consider changing this to be a mutable reference
  |
1 | fn add_world(text: &mut String) {
  |                     +++

Apply your lesson 1.7 training. The headline says we tried to mutate something "behind a & reference". The label under the caret restates it as a plain sentence: this is a & reference, so writing through it isn't allowed. And the help block, as usual, is the compiler reading ahead in the syllabus: there exists something called a mutable reference, spelled &mut, and it would make this program legal. That's the next lesson, and you've technically been using it since lesson 1.6.

Defaulting to read-only is the same design taste you met in lesson 1.4, where variables are immutable until you say mut. A function that takes &String is making a promise the compiler will hold it to: I only look. When you call it, you can hand over a view of your most precious data without reading the function's body first, because the signature already told you the worst it can do.

The two rules

Everything this chapter does flows from two rules, so here's the map before we walk the terrain:

  1. At any given time, a value can have either one mutable reference or any number of immutable references.
  2. References must always be valid.

Rule 1 is the subject of lessons 9.2 and 9.3, and rule 2 belongs to lesson 9.5. The part of the compiler that enforces them has a name you've been promised since chapter 5: the borrow checker. By the end of the chapter you'll have seen it refuse half a dozen programs, and the point of every refusal is the same: each one is a real bug, of a kind that some very expensive C++ ships with.

Key insight

Any number of & references to the same value can exist at once, and that's fine, for the same reason ten neighbors can watch one bicycle. Readers don't interfere with each other. It's writing that needs rules, and that's precisely where Rust draws the line.

Quiz time

Question #1

In let len = measure(&message);, what does the & do, and what does measure own while it runs?

Show solution

The & creates a reference to message and lends it to measure: borrowing, not giving. While it runs, measure owns nothing of message's. Its parameter is a view; message in main remains the owner throughout, which is why main can keep using it after the call.

Question #2

What does this program print? (Compare it with lesson 8.4 before answering.)

fn main() {
    let city = String::from("Nairobi");
    let view = &city;
    println!("{view}");
    println!("{city}");
}
Show solution
Nairobi
Nairobi

In lesson 8.4, let s2 = s1; moved the value and ended s1. Here let view = &city; merely borrows it, so city stays alive and usable. One character is the whole difference between "gave it away" and "let someone look".

Question #3

Predict the compiler's reaction, then give the one-character fix:

fn measure(text: &String) -> usize {
    text.len()
}

fn main() {
    let message = String::from("hello");
    let len = measure(message);
    println!("{message} is {len} bytes long");
}
Show solution

error[E0308]: mismatched types at the call: expected &String, found String, with the compiler's help line offering consider borrowing here, i.e. measure(&message);. The signature asks for a loan and the call offers a gift; the types disagree, so the program is refused before the move (and the use-after-move on the next line) can even be judged. Add the &.

A read-only window covers functions that look. It doesn't cover read_line, which has been writing into your Strings since chapter 1 on an IOU. Next lesson, the &mut tab finally gets settled.