8.1Introduction to ownership
Lesson 0.3 made you a promise: "The Rust compiler enforces a set of rules about how memory is used (you'll meet them as the ownership system, starting in chapter 8)." Welcome to chapter 8. This is the chapter the course has been steering toward since its first page, the chapter that explains why Rust exists, and the chapter where Rust stops being "a nice language with strict types" and becomes something no other mainstream language is.
It's also the chapter where some readers have historically given up on Rust, so a word about pacing before anything else. The lessons in this chapter are deliberately small. Each one adds a single idea, and each idea is checkable against code you've already written. If a lesson feels short, that's the design working. You've read something like two hundred compiler errors by now; nothing arriving in this chapter is harder than what you've already survived, and most of it explains things you've been politely asked to take on credit.
The problem: memory has to be given back
Here's the situation every programming language has to deal with.
Some values have a size the compiler can't know in advance. Lesson 5.3 introduced the obvious example: a String grows at runtime, holding however much text the user happens to type. Memory for values like that has to be requested while the program runs, from a part of memory built for the purpose (next lesson gives it a name and a floor plan).
Requested memory must eventually be given back. A program that requests and never returns will eat more and more memory the longer it runs. And "given back" turns out to be a surprisingly delicate operation, because it has to happen exactly once, and only after the memory's last use. Every way of getting it wrong has a name and a long criminal record:
- Give it back, then use it anyway: a use-after-free. The memory may already belong to someone else; reading it yields garbage, writing it corrupts whatever now lives there.
- Give it back twice: a double free. The bookkeeping that tracks free memory is corrupted, and the program fails somewhere far away from the actual mistake.
- Never give it back: a memory leak. No single dramatic failure, just a program that slowly swells until the machine runs out.
If these sound like the villains from lesson 0.3, they are. "Use memory after freeing it" was the exact example of how a C or C++ program "corrupts data, crashes mysteriously, or opens a security hole." The question of who gives memory back, and when is one of the central design problems of programming languages, and the industry has produced three answers.
Answer one: trust the programmer
C and C++ make freeing memory the programmer's job. You request memory when you need it, and you write the code that gives it back when you're done. Nothing checks your work.
The upside is real: no overhead, no machinery running behind your back, complete control. When everything is done correctly, nothing is faster. The downside is that "when everything is done correctly" is doing heroic work in that sentence. Every use-after-free, double free, and leak in forty years of C and C++ software was written by a programmer who believed, at the time, that they were doing it correctly. Decades of study after study have found that the majority of serious security vulnerabilities in large C and C++ codebases are memory-safety bugs: precisely the categories listed above.
Answer two: pay a janitor
Java, Python, C#, Go, and most other modern languages take the job away from the programmer entirely. Lesson 0.3 described their tool: a garbage collector, "a system that periodically finds and frees memory your program is no longer using, while the program runs."
This genuinely solves the safety problem. You cannot double-free; you can't free at all. But the costs are also real. The collector is a program running inside your program, spending memory and processor time on bookkeeping. And it runs on its schedule, not yours: when a collection pass lands at the wrong moment, your program stutters. For a web page that's usually fine. For a game rendering a frame every 16 milliseconds, a car's brake controller, or a server with strict response-time promises, "the program briefly stops at unpredictable times" can be disqualifying.
So the two classic answers trade against each other: control and speed with no safety net, or safety with a runtime cost and a loss of control.
Answer three: decide at compile time
Rust's answer is that the compiler figures out where every "give it back" belongs, while compiling, and writes it into the program for you.
For that to be possible, the compiler must be able to reason about which part of the program is responsible for each value, so Rust has rules. They're called the ownership rules, and here is the entire list:
- Every value has an owner: a single variable responsible for it.
- There is exactly one owner at a time.
- When the owner goes out of scope, the value is dropped: its memory is given back.
That's the whole system. Three rules, each of which this chapter unpacks at the pace of roughly one lesson per rule: dropping and scope in lesson 8.3, the "one owner at a time" rule and what happens when a value changes hands in 8.4 through 8.6, and the escape hatches in 8.5 and 8.7.
The result has no parallel in the other two answers. The memory-safety bugs are gone, like with a garbage collector, but there is no collector: no background process, no pauses, no runtime cost. Cleanup happens at fixed places in your code, decided before the program ever runs. The price is that the rules are enforced, and code that breaks them doesn't compile. You've seen this trade before, in miniature, in lesson 1.4: declaring intent (mut) so the compiler can reject what you didn't intend. Ownership is the same philosophy applied to the harder problem.
Key insight
Manual management makes memory bugs possible. Garbage collection makes them impossible at runtime, for a runtime price. Ownership makes them impossible at compile time, for free at runtime. The cost moved out of your running program and into your compile, which is also why the compiler errors in this chapter exist: each one is a memory bug that didn't happen.
What this means for the code you've written
Almost nothing, and that's worth saying plainly before the chapter gets going.
Integers, floats, bools, and chars are unaffected by everything above. They have a fixed size, they live entirely in their variable's box (lesson 1.3), and nothing about them needs giving back. Every program from chapters 1 through 7 behaves exactly as you learned. Lesson 8.5 makes precise why the simple types get to opt out of the drama.
The type that makes ownership visible is one you already know well: String, the first type this course showed you that owns memory requested at runtime. String is the lab animal for the whole chapter.
Quiz time
Question #1
Name the three classic memory bugs described in this lesson, and for each, say in one sentence what goes wrong.
Show solution
A use-after-free gives memory back and then uses it anyway, reading garbage or corrupting whatever now occupies it. A double free gives the same memory back twice, corrupting the system's bookkeeping of what's free. A memory leak never gives memory back, so the program's memory use grows until something runs out.
Question #2
Match each strategy to its cost: manual memory management, garbage collection, ownership.
a) Pays with processor time and unpredictable pauses while the program runs. b) Pays at compile time: programs that can't be proven safe are rejected. c) Pays in risk: nothing checks that memory is given back correctly.
Show solution
Manual management is (c): full speed and control, but every guarantee rests on the programmer being right. Garbage collection is (a): safe, but a collector runs inside your program on its own schedule. Ownership is (b): the checking moved into the compiler, so the running program pays nothing, and the bill arrives as compiler errors instead.
Question #3
Rust's ownership rules say every value has exactly one owner at a time. Without reading ahead: which rule of thumb from earlier chapters does this most resemble, in spirit? (Hint: lesson 1.4.)
Show solution
It's the mut philosophy again: state your intent in the code, and let the compiler enforce it. mut declares "this variable will change" so that accidental change becomes a compile error. Ownership declares "this variable is responsible for this value" so that accidental misuse of memory becomes a compile error. In both cases a whole category of bugs is moved from runtime (where it hurts) to compile time (where it's a red squiggle).
Next lesson builds the floor plan all of this runs on. You've known about the call stack since lesson 2.1 and you've watched it in a debugger in lesson 3.5; it's time to learn what it's actually made of, and to meet its opposite number: the heap.