On this page
Ownership Model
Rust’s ownership model consists of only three rules: each value has a single owner, values are dropped when the owner goes out of scope, and assignment and passing arguments transfer ownership (move). Copy is an exception to move (both variables are usable after a bitwise copy), Clone is an explicit deep copy, and partial move allows you to move out specific fields from a struct. These three rules are the foundation of how Rust guarantees memory safety without a garbage collector.
Why Rust Needs Ownership
C’s manual malloc/free and C++’s RAII both rely on programmer discipline—forgotten releases, double frees, and use-after-free errors are completely invisible at compile time. GC languages manage memory automatically but introduce runtime overhead and stop-the-world latency. Rust’s answer: guarantee memory safety at compile time through ownership rules, with zero runtime overhead.
The core three rules:
- Each value in Rust has exactly one owner
- When the owner goes out of scope, the value is dropped
- Assignment/passing arguments/return values transfer (move) ownership, unless the type implements Copy
Move Semantics
let s1 = Stringfrom;
let s2 = s1; // s1's ownership is moved to s2
// println!("{}", s1); // ERROR: s1 has been moved, cannot be used anymore
String stores three words (pointer, length, capacity) on the stack, with the actual data on the heap. let s2 = s1 performs a shallow copy (copying the 3 words on the stack), then marks s1 as invalid—no double free occurs because only s2 will call drop to free the heap memory when it goes out of scope.
The pseudo-code actually generated by the compiler:
s1 = String { ptr → heap[5 bytes "hello"], len: 5, cap: 5 }
s2 = String { ptr → same heap, len: 5, cap: 5 } // move: copy stack fields
s1 is marked as "moved out" → drop(s1) is skipped by the compiler
Copy vs Clone
let x = 42;
let y = x; // x is still usable — i32 implements Copy
println!; // OK
The Copy trait is a marker automatically recognized by the compiler—types that implement Copy perform a bitwise copy rather than a move during assignment. The condition is: all fields of the type must implement Copy. i32, bool, char, &T implement Copy; String, Vec<T>, Box<T> do not—because they have heap data.
Clone is an explicit deep copy: .clone() is called by the programmer and can allocate new heap memory. String implements Clone but not Copy—an explicit .clone() produces a new String without transferring ownership.
let s1 = Stringfrom;
let s2 = s1.clone; // Deep copy: new heap allocation
println!; // Two independent Strings, both valid
Where Moves Occur
// 1. Assignment
let s2 = s1; // move
// 2. Passing arguments
// s takes ownership
take; // s1 moves into s → s1 becomes invalid
// 3. Return values
// Ownership transfers to caller with the return value
// 4. Pattern matching
let = ; // Both Vecs move into a and b respectively
// 5. Closure capture
let v = vec!;
let closure = ; // Closure captures v by move (because Vec doesn't implement Copy)
// v can no longer be used externally!
Stack Data vs Heap Data
The key to understanding move and copy: bitwise copying of stack data is safe—ownership transfer only requires copying stack contents. Transferring ownership of heap data also only requires copying the pointer on the stack; the heap data itself is not copied.
let s1 = String::from("hello"); // stack: [ptr|len|cap], heap: "hello"
let s2 = s1; // stack copy: [same ptr|same len|same cap]
// s1 marked as moved → drop(s1) is not called
// End of scope: drop(s2) → frees "hello" on heap
Conditions for Copy
Which types automatically implement Copy?
- All fields implement Copy
- Does not implement Drop (if it has Drop, it means there are resources that need releasing—cannot be a simple bitwise copy)
// Manually derive Copy (also requires Clone)
// All fields are Copy → Point is also Copy
; // String is not Copy → MyString is not Copy
// Cannot derive Copy for MyString!
Partial Move
let p = ;
let = p; // s takes ownership of the String, n copies 42
// println!("{:?}", p); // ERROR: p.0 has been moved
println!; // OK: p.1 is i32, copied, still in p
Drop Order
When a scope ends, variables are dropped in reverse order of declaration:
// drop(b) → drop(a)
This is important for safety—later variables may reference earlier ones; dropping later variables first ensures references remain valid.
References
- Rust Book: Chapter 4.1 — What is Ownership
- Rust Reference: destructors, move semantics
- RFC: RFC 213 (default Move semantics)
Keywords: ownership, move, Copy, Clone, stack, heap, drop, partial move, RAII