Recommended resources
The freely available resources this lesson will direct you to.
Hello! In the previous lesson, you used &String to let a function inspect text while the caller kept ownership. That works for reading, but a function sometimes needs to update the caller’s value. This lesson introduces mutable references: temporary write access without an ownership transfer. You’ll see how to use one, why Rust requires that access to be exclusive, and when the owner can use its value again.
Granting write access without giving away ownership
Suppose a function changes a request status. Passing an owned String would move it into the function. Instead, the caller can lend the function mutable access:
fn mark_processing(status: &mut String) {
status.push_str(" for processing");
}
fn main() {
let mut status = String::from("queued");
mark_processing(&mut status);
println!("{status}"); // queued for processing
}
Three pieces work together. let mut status allows the owner’s value to change. At the call site, &mut status creates a mutable reference. In the signature, &mut String says that mark_processing receives that kind of reference, not ownership of a String. The function changes the original value; after the call, status still belongs to main.
You do not need mut before the parameter name. The reference permits changes through it, such as status.push_str(...); the function is not reassigning its status parameter to refer to a different string.
Watch Niko Matsakis’s “Mutable borrows” for a visual account of this temporary handover. Focus on what happens to the owner’s access while the function holds the reference.
“Mutable borrows,” by Niko Matsakis, illustrates how a function can change an owned value without becoming its owner.
Watch the mutable handover for the &mut syntax and the temporary loss of direct access through the owner. Then watch the access rules, especially the code demonstration beginning around 00:04:27: notice why a second active mutable reference fails and why a later, separate borrow succeeds.
The diagram below offers a compact way to remember the contrast with last lesson. It is a schematic picture of access, not a claim that every Rust value has this heap layout.

Why a writer must be alone
Aliasing means reaching the same data through different references or variables. Several aliases can safely read a value together, as you saw with immutable references. But if one path can change the data while another path is still relying on it, the reader could observe an unexpected change—or, for some data structures, hold a reference into storage that the change invalidates.
Rust therefore applies this rule to an actively borrowed value: either one mutable reference or any number of immutable references, but not both at once. This applies even in a single-threaded program. Preventing data races is one benefit; preventing conflicting access more generally is the point.
The official Rust book’s “Mutable References” subsection shows the compiler errors behind this rule. Its rendered code examples are useful to inspect alongside the prose, particularly the locations the compiler identifies as the first borrow and the later conflicting use.
References and Borrowing - The Rust Programming ...
Read the “Mutable References” subsection of The Rust Programming Language for the syntax, the exclusivity rule, and the compiler’s explanation of conflicting borrows.
In “Mutable References,” begin just after the first corrected change example, at the paragraph starting “First, we change.” Read the mutable-borrow explanation, including the error for two mutable references. Later in the same subsection, start at “Rust enforces a similar rule for combining mutable and immutable references.” Read the mixed-borrow examples, stopping before “Dangling References.” Focus on which later use keeps each borrow active.
Here is the two-writers conflict in a small program that does not compile:
fn main() {
let mut status = String::from("queued");
let first = &mut status;
let second = &mut status; // Error: first is still needed.
first.push_str(" for processing");
second.push_str(" again");
}
The crucial detail is not merely that first and second are declared in the same block. The later call through first means its borrow must remain active when second is created. Rust cannot grant both references exclusive access to status at that time.
A reader and writer conflict for the same reason:
fn main() {
let mut status = String::from("queued");
let view = &status;
let editor = &mut status; // Error: view will be used again.
println!("{view}");
editor.push_str(" for processing");
}
view promises a shared, read-only view through its use in println!. Creating editor before that use finishes would grant write access while the shared borrow is still active. Direct access through the owner is restricted too: if an active &mut status will be used later, you cannot slip in a println!("{status}") through the owner. For that interval, the mutable reference is the route to the borrowed value.
Exclusive access is temporary
An exclusive borrow is not a permanent transfer of control. This version compiles because each use finishes before the next kind of access begins:
fn main() {
let mut status = String::from("queued");
let view = &status;
println!("Before: {view}"); // Last use of view.
let editor = &mut status;
editor.push_str(" for processing"); // Last use of editor.
println!("After: {status}");
}
Although view and editor are declared in the same block, Rust tracks where each reference is last used. The shared borrow is over before editor is created. The mutable borrow is over before the owner is printed. When reasoning about an error, look beyond the line that creates a reference: find its final use.
Braces can make the intended separation particularly clear:
fn main() {
let mut status = String::from("queued");
{
let editor = &mut status;
editor.push_str(" for processing");
} // editor cannot be used beyond this block.
println!("{status}");
}
Function calls usually give you this separation without an extra block. Once mark_processing(&mut status) returns, that call’s mutable borrow has ended, and the caller can inspect or borrow status again. Ownership has stayed with the caller throughout.
Mutable references are useful for numbers and struct fields too. For a number, * explicitly accesses the value behind the reference:
fn add_accepted(count: &mut u32, additional: u32) {
*count += additional;
}
fn main() {
let mut accepted = 3;
add_accepted(&mut accepted, 2);
println!("{accepted}"); // 5
}
The parameter count is a reference; *count is the u32 it refers to. In the earlier string example, you did not write (*status).push_str(...): Rust lets a method call reach the String through its reference. These are two forms of the same underlying idea—modify the borrowed value, not a new owned copy.
For a backend application, this distinction will matter whenever one part of a program updates state owned elsewhere. At this stage, the safe default is straightforward: use a shared reference for inspection, a mutable reference for an in-place update, and keep each borrow no longer than you need it.
Conclusion
&mut value lends temporary write access, and a parameter such as &mut String or &mut u32 accepts it without taking ownership. While that mutable borrow is active, competing access to the same value—through another mutable reference, a shared reference, or the owner—is restricted. Once the borrow’s last use has passed, the owner can access the updated value again.
Next, you’ll apply borrowing to slices, which let a reference describe a portion of an array or string rather than requiring ownership of the whole value.