Ownership and Borrowing
Every program has to answer the same question: when is a piece of memory no
longer needed, so it can be given back? Some languages leave it to you, and a
forgotten or doubled free becomes a crash or a security hole. Others run a
garbage collector that finds unused memory while the program runs.
Rust does neither. It answers the question at compile time with a small set of
rules called ownership. This lesson covers those rules, what happens when
you assign or pass a value, how to use a value without taking it with
references, and why Rust has two string types, String and &str.
Ownership is the idea that makes Rust feel different from other languages, and the compiler errors it produces are the ones beginners meet most. Take your time with this one.
The ownership rules
There are three of them:
- Every value has an owner, the variable that holds it.
- A value has exactly one owner at a time.
- When the owner goes out of scope, the value is dropped and its memory is freed.
A scope is the block a variable lives in, from its let to the closing brace:
fn main() {
let outer = String::from("outer");
{
let inner = String::from("inner");
println!("{outer} and {inner}");
} // `inner` goes out of scope here, and its String is dropped
println!("{outer} is still here");
} // `outer` is dropped hereouter and inner
outer is still hereRust inserts the cleanup at the closing brace for you. There is no free to
forget, and nothing can free the same memory twice, because only the one owner
does it.
String owns its text
The examples use String because it is the simplest type where ownership
matters. A String is a growable piece of text. Its characters live in memory
on the heap, which is allocated while the program runs, and the String
variable itself holds a pointer to that memory, a length and a capacity.
fn main() {
let mut title = String::from("Rust");
title.push_str(" in Action");
println!("{title} has {} bytes", title.len());
}Rust in Action has 14 bytesIntegers, floats, bool and char are different: they have a fixed, small
size, so the whole value is stored directly in the variable. That difference
explains the next two sections.
Moves
What happens when you assign a String to another variable?
fn main() {
let first = String::from("ferris");
let second = first;
println!("{second}");
}ferrisOnly the pointer, length and capacity are copied. The text on the heap is not,
so now two variables would point at the same memory. If both were still valid,
both would free it at the end of main. Rust prevents that by treating the
assignment as a move: ownership passes to second, and first can no
longer be used. Using it afterwards is a compile error, shown in the common
mistakes below.
Passing a value to a function moves it too. The parameter becomes the new owner, and the value is dropped when the function returns:
fn announce(name: String) {
println!("Now playing: {name}");
} // `name` is dropped here
fn main() {
let song = String::from("Crab Rave");
announce(song);
// `song` was moved into `announce` and can't be used here
}Now playing: Crab RaveA function can hand ownership back by returning the value:
fn add_exclamation(mut text: String) -> String {
text.push('!');
text
}
fn main() {
let cheer = String::from("Go");
let cheer = add_exclamation(cheer);
println!("{cheer}");
}Go!This works, but passing everything in and back out would get tiring fast. References, a few sections down, are the real answer.
Cloning
When you do want two independent copies of heap data, ask for one with
.clone():
fn main() {
let original = String::from("draft");
let mut copy = original.clone();
copy.push_str(" v2");
println!("{original} / {copy}");
}draft / draft v2clone allocates new memory and copies the text into it, so each variable
owns its own String. It is explicit on purpose: when you see .clone(), you
know work is being done. Cloning to silence the compiler is a common beginner
habit; before reaching for it, check whether a reference would do.
Copy types
Simple values don't move. They are copied, and both variables stay usable:
fn main() {
let a = 42;
let b = a;
let point = (1.5, -2.0);
let moved_point = point;
println!("{a} {b} {point:?} {moved_point:?}");
}42 42 (1.5, -2.0) (1.5, -2.0)Types that behave like this implement the Copy trait. All the integer and
float types, bool and char are Copy, and so are tuples and arrays made
only of Copy types. Copying them is as cheap as moving would be, and they own
no heap memory, so there is nothing to free twice. That is why the countdown
example in the functions lesson could change its parameter without touching the
caller's variable.
String is not Copy, and neither is anything that contains one.
References and borrowing
Often a function only needs to look at a value. Instead of moving the value in,
pass a reference to it with &. The function borrows the value; the
caller keeps ownership:
fn count_vowels(text: &String) -> usize {
let mut count = 0;
for c in text.chars() {
if "aeiouAEIOU".contains(c) {
count += 1;
}
}
count
}
fn main() {
let phrase = String::from("Borrow checker");
let vowels = count_vowels(&phrase);
println!("{phrase} has {vowels} vowels");
}Borrow checker has 4 vowelsThe parameter type &String means "a reference to a String", and the call
passes &phrase. Because count_vowels never owned the String, nothing is
dropped when it returns, and phrase is still usable afterwards. A reference
is just a pointer to the value, so making one is cheap.
Methods borrow the same way. phrase.len() borrows phrase for the length of
the call, which is why you can keep using phrase after calling it.
A reference is read-only by default. Trying to change the borrowed value through it doesn't compile.
Mutable references
To let a function change a value it doesn't own, pass a mutable reference,
&mut. The variable itself must be declared mut:
fn add_bonus(score: &mut u32) {
*score += 10;
}
fn shorten(text: &mut String) {
text.truncate(5);
}
fn main() {
let mut score = 75;
add_bonus(&mut score);
let mut headline = String::from("Breaking news");
shorten(&mut headline);
println!("{score} {headline}");
}85 BreakThe * in *score += 10 dereferences the reference: it means "the value
score points to", not the reference itself. Method calls like
text.truncate(5) dereference automatically, so you rarely write * with
them.
The &mut shows up at both ends, in the signature and at the call. Anyone
reading shorten(&mut headline) can tell that headline may change.
The borrowing rules
References come with two rules, checked by the part of the compiler called the borrow checker:
- At any moment you can have either one mutable reference or any
number of shared (
&) references to a value, but not both. - A reference must never outlive the value it points to.
Many readers or one writer. As long as nobody can change a value, any number of readers can look at it safely. While someone is changing it, nobody else may read or write it, or they might see it half-changed:
fn main() {
let mut level = String::from("easy");
let a = &level;
let b = &level;
println!("{a} {b}");
let c = &mut level;
c.push_str(" mode");
println!("{c}");
}easy easy
easy modeThis compiles even though c is created while a and b are still in scope.
A borrow lasts from where the reference is created to where it is last
used, not to the end of the block. a and b are last used in the first
println!, so by the time c borrows level mutably, they are finished.
If a were used again after c was created, the borrows would overlap and the
code would not compile. When the compiler complains about overlapping borrows,
the fix is usually to finish with one reference before creating the other, or
to limit a reference to a smaller block.
The owner is bound by the rules as well. While a shared reference is in use, even the owner can't change the value, and while a mutable reference is in use, the owner can't read it.
The second rule stops dangling references, pointers to memory that has already been freed. A function can't return a reference to a local variable, because the local is dropped when the function returns. Return the owned value instead.
String and &str
Rust has two main string types, and ownership is the difference between them:
Stringowns its text on the heap. It can grow and shrink, and it is freed when its owner goes out of scope.&str, a string slice, borrows text that some other value owns. It is a pointer and a length, and it can't change the text.
String literals like "hello" are &str. Their text is stored in the compiled
program itself, so they are valid for the whole run. The slices lesson shows
how to take a &str that points at part of a String.
To get a String from a &str, use String::from or .to_string(). To get a
&str from a String, borrow it: &name or name.as_str().
When a function only reads text, take &str rather than &String. A
&String is converted to &str automatically, so the function then accepts
both:
fn initials(full_name: &str) -> String {
let mut result = String::new();
for word in full_name.split_whitespace() {
if let Some(first) = word.chars().next() {
result.push(first);
}
}
result
}
fn main() {
let owned = String::from("Grace Brewster Hopper");
println!("{}", initials(&owned));
println!("{}", initials("Ada Lovelace"));
}GBH
ALinitials returns a String because it builds new text; returning a &str
would need something to own that text. (if let and Some come in the enums
and Option lessons. Here they mean "if the word has a first character, call
it first".)
Bytes and characters
Rust strings are UTF-8, so one character can take between one and four bytes.
len() counts bytes, not characters:
fn main() {
let word = "naïve";
println!("{} bytes", word.len());
for c in word.chars() {
print!("[{c}]");
}
println!();
}6 bytes
[n][a][ï][v][e]ï takes two bytes, so the five-character word is six bytes long. When you
care about characters rather than storage, walk them with .chars(), which
yields each char in turn. The closures and iterators lesson covers the many
things you can do with what .chars() returns.
Common mistakes
Using a value after it moved
fn main() {
let playlist = String::from("Road trip");
let saved = playlist;
println!("{playlist} is saved as {saved}");
}The compiler points at the line that moved the value and the line that used it
afterwards. Use saved instead, borrow with let saved = &playlist;, or clone
if you really need two copies. Passing a String to a function that takes
String moves it the same way; change the parameter to &str if the function
only reads it.
Changing a value while it is borrowed
fn main() {
let mut name = String::from("Ferris");
let first_look = &name;
name.clear();
println!("{first_look}");
}name.clear() needs a mutable borrow of name, but first_look is still
used on the next line. If this compiled, first_look would show text that had
just been erased. Finish using the shared reference before changing the value.
Two mutable references at once
fn main() {
let mut total = 0;
let first = &mut total;
let second = &mut total;
*first += 1;
*second += 1;
println!("{total}");
}Only one mutable reference may be in use at a time. Here one is enough: change
total through first twice, or skip the references and write to total
directly.
Borrowing mutably from an immutable variable
fn reset(counter: &mut u32) {
*counter = 0;
}
fn main() {
let clicks = 12;
reset(&mut clicks);
println!("{clicks}");
}A mutable reference can only come from a mutable variable. Declare it with
let mut clicks.
Returning a reference to a local
fn make_greeting() -> &String {
let greeting = String::from("hello");
&greeting
}
fn main() {
println!("{}", make_greeting());
}greeting is dropped when make_greeting returns, so the reference would
point at freed memory. The error talks about lifetimes, which get their own
lesson later. Ignore the 'static suggestion, which is for values that live
for the whole program; the second one is the fix: return the String itself,
-> String and greeting without the &.
Summary
- Every value has one owner, and it is dropped when the owner goes out of scope.
- Assigning or passing a
String(or anything else that isn'tCopy) moves it; the old variable can't be used..clone()makes an independent copy. - Integers, floats,
bool,char, and tuples and arrays of them areCopy: assigning them copies the value. &valueborrows without taking ownership;&mut valueborrows so the value can be changed, and needs amutvariable.*reaches the value behind a reference.- Either one
&mutor any number of&at a time, and a borrow lasts until the reference's last use. References can't outlive their value. Stringowns its text;&strborrows it. Take&strin functions that only read text.len()counts bytes;.chars()walks characters.
Practise what you read
Challenges that use the ideas from this lesson.