Why does Rust have ownership?
Short answer: ownership exists to make two memory bugs impossible — freeing the same allocation
twice, and reading memory after it has been freed. In C both are undefined behaviour, because free
is a call you make yourself. A garbage collector rules them out too, but it does the work while the
program runs; ownership does it before the program starts.
What C leaves to you
Section titled “What C leaves to you”The Rust book states the requirement
in one line: “We need to pair exactly one allocate with exactly one free.” Nothing in C ties a
pointer to the allocation it came from, so both ways of breaking that pairing are left to the standard
to describe:
The behavior is undefined if the memory area referred to by
ptrhas already been deallocated …
The behavior is undefined if after
free()returns, an access is made through the pointerptr.
Both are from cppreference’s page for free: a double
free, and a use-after-free. Undefined behaviour isn’t an error message, it’s the standard declining to
make a promise. In practice the allocator’s bookkeeping gets corrupted, or the address is reused while
something still points at it. The book calls that “one of the memory safety bugs” and says it “can
lead to memory corruption, which can potentially lead to security vulnerabilities.”
The other answer is a garbage collector
Section titled “The other answer is a garbage collector”A GC takes the free away from you. Your code allocates, the collector works out what’s still reachable
and reclaims the rest. There’s no second free call to write, and an object you can still reach is by
definition still alive, so neither bug has anywhere to happen.
The bill arrives while the program runs. Finding out what’s still in use is work: the collector pauses to walk the heap, or the runtime keeps a reference count up to date on every assignment.
Ownership moves the check to the compiler
Section titled “Ownership moves the check to the compiler”One owner per value means the free has exactly one place it can go — the end of that owner’s scope. A value that has been moved out of a variable can’t be read through it, so the pointer that would dangle never exists. The book calls this “a third approach”, and notes that “None of the features of ownership will slow down your program while it’s running.”
flowchart TB S["who frees the memory?"] --> A["C: you do"] S --> B["a GC: the runtime does"] S --> C["Rust: the compiler decides where"] A --> A1["free twice, or free too early"] B --> B1["counted or swept while the program runs"] C --> C1["one free, at the end of the owner's scope"]
What it costs
Section titled “What it costs”The price is paid while you write the code rather than while it runs. Programs a GC language would
accept get rejected, and the complaint is about ownership instead of about what you meant. Two limits:
ownership can’t stop a leak, since keeping a value alive wastes memory but corrupts nothing, and the
guarantees are the ones safe code gets — unsafe puts raw pointers back in your hands.
Related
Section titled “Related”- What is ownership in Rust? — the rule these bugs are the reason for
- Why does Rust move values instead of copying them? — what a move does to the value
- How to fix error E0382: borrow of moved value — the error the rules produce