TL;DR: Ownership is Rust's system for managing memory without a garbage collector. Every value has exactly one owner. When the owner goes out of scope, the value is dropped automatically. This eliminates memory leaks, dangling pointers, and data races at compile time, with zero runtime cost.
What Is Ownership in Rust?
Ownership is a set of compile-time rules that governs how Rust programs manage memory, replacing both garbage collection and manual memory management with a system that is both safe and fast.
Every other mainstream language picks one of two approaches: garbage collection (Java, Python, Go) pauses the program to reclaim unused memory; manual management (C, C++) gives the programmer full control but enables bugs. Rust does neither. Instead, the compiler tracks ownership and frees memory automatically when values go out of scope.
The result: memory safety with the performance of C.
What Are the Three Rules of Ownership?
Rust's ownership model is governed by exactly three rules that the compiler enforces at every build:
- Each value has exactly one owner, a variable that holds it
- There can only be one owner at a time, ownership can be moved but not shared
- When the owner goes out of scope, the value is dropped, memory is freed automatically
fn main() {
let s1 = String::from("hello"); // s1 owns the String
let s2 = s1; // ownership moves to s2; s1 is now invalid
// println!("{s1}"); // ❌ compile error: s1 no longer owns the value
println!("{s2}"); // ✅ s2 is the owner
} // s2 goes out of scope; String is dropped, memory freedThis is called a move. Unlike a copy, the original variable becomes invalid after a move.
What Is the Difference Between Move and Copy?
Types that are cheap to duplicate (integers, booleans, floats) implement the Copy trait and are copied instead of moved, the original remains valid.
fn main() {
let x = 5; // i32 implements Copy
let y = x; // x is copied, not moved
println!("{x}"); // ✅ x is still valid
println!("{y}"); // ✅ y has its own copy
}Types that manage heap memory (String, Vec, Box) do not implement Copy, they move. If you need both variables to remain valid, use .clone():
let s1 = String::from("hello");
let s2 = s1.clone(); // deep copy; s1 and s2 are independent
println!("{s1}"); // ✅ still valid
println!("{s2}"); // ✅ also validHow Does Ownership Work With Functions?
Passing a value to a function moves or copies it, the same rules that apply to variable assignment apply to function calls.
fn take_ownership(s: String) {
println!("{s}");
} // s is dropped here
fn make_copy(n: i32) {
println!("{n}");
} // n is dropped, but the caller's copy is unaffected
fn main() {
let s = String::from("hello");
take_ownership(s); // s is moved into the function
// println!("{s}"); // ❌ s is no longer valid here
let x = 5;
make_copy(x); // x is copied
println!("{x}"); // ✅ x is still valid
}To avoid moving ownership into a function, use references instead, see the Borrow Checker glossary entry.
Why Does Rust Use Ownership Instead of Garbage Collection?
Ownership gives Rust deterministic, zero-cost memory management, memory is freed at a predictable point (scope exit) with no runtime pauses and no overhead.
| Approach | Runtime cost | Safety | Predictability |
|---|---|---|---|
| Garbage collection | GC pauses, memory overhead | Safe | Unpredictable |
Manual (malloc/free) | Zero | Unsafe, leaks, UAF bugs | Manual |
| Rust ownership | Zero | Safe, enforced by compiler | Deterministic |
This is why Rust is used for latency-sensitive systems: game engines, databases, network proxies, and OS kernels where GC pauses are unacceptable.
Frequently Asked Questions
Related but distinct. Ownership defines who is responsible for a value. The borrow checker enforces rules around lending references to values without transferring ownership. Together they form Rust's memory safety system.
Because Rust would not know which owner is responsible for freeing the memory, double-free errors are a common source of security vulnerabilities in C++. Single ownership eliminates this class of bug entirely.
When a value is dropped, Rust calls its Drop implementation (if any) and frees its memory. This happens automatically when the owning variable goes out of scope. You can also drop manually with drop(value).
Yes, but stack memory is freed automatically when a function returns regardless. Ownership rules matter most for heap-allocated values (String, Vec, Box) where the programmer would otherwise need to manage memory manually.
Sources
- The Rust Book ; Chapter 4: Ownership: The definitive ownership reference
- Rustonomicon ; Ownership Model: Deep dive for advanced readers
Related Glossary Terms
- Borrow Checker: How Rust enforces safe references
- Lifetime: How long references are valid
- Box: Heap allocation in Rust
- Reference: Ownership and borrowing meet through
&Tand&mut T - Copy/Clone: The traits that determine when values move, copy, or duplicate
- Interior Mutability: Patterns that bend ownership rules without breaking Rust's guarantees
- const-static: Global values and
'staticdata still obey Rust's ownership model - RefCell: RefCell is one of the core ownership escape hatches for single-threaded mutable access
Keep Reading
- Rust Ownership and Borrowing Explained: the definitive article on ownership, borrowing, and moves
- Rust Lifetimes Deep Dive: lifetimes extend ownership rules to references
- Rust Memory Safety: NSA and CISA: why ownership is a national security advantage
