Skip to content
Select theme

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.

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 ptr has already been deallocated …

The behavior is undefined if after free() returns, an access is made through the pointer ptr.

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.”

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.

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"]

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.