Ownership
Move vs. Copy vs. Clone
Rust provides three distinct mechanisms for transferring or duplicating values across bindings and function boundaries: Move, Copy, and Clone. Understanding how they differ in syntax, runtime cost, and ownership transfer is central to writing idiomatic Rust.
Side-by-Side Comparison
| Dimension | Move | Copy (std::marker::Copy) |
Clone (std::clone::Clone) |
|---|---|---|---|
| Trigger syntax | Implicit (let b = a; on non-Copy types) |
Implicit (let b = a; on Copy types) |
Explicit method call (let b = a.clone();) |
| Source variable after operation | Invalidated (cannot be read or dropped) | Remains valid and independent | Remains valid and independent |
| What happens in memory | Shallow bitwise copy of stack header (memcpy) |
Shallow bitwise copy of stack value (memcpy) |
Arbitrary code (often deep heap allocation + copy) |
| Runtime cost | Always $O(1)$ (few stack bytes) | Always $O(1)$ (few stack bytes) | Variable ($O(1)$ to $O(n)$ heap allocation) |
Can implement Drop? |
Yes | No (E0184—mutually exclusive with Drop) |
Yes |
1. Move (`let b = a;` for String): `a` (invalidated) `b` ──> [Heap: "rust"] (1 buffer)
2. Copy (`let b = a;` for i32): `a` = 42 `b` = 42 (2 stack ints)
3. Clone (`let b = a.clone();` String): `a` ──> [Heap: "rust"] `b` ──> [Heap: "rust"] (2 heap buffers!)
Demonstrating All Three Together
#[derive(Debug, Clone, Copy)]
struct Point(i32, i32); // Implements both `Copy` and `Clone`
fn main() {
// 1. Copy: implicit bitwise duplication on stack
let p1 = Point(3, 4);
let p2 = p1;
println!("Copy: p1 = {p1:?}, p2 = {p2:?}");
// 2. Move: implicit ownership transfer; `s1` is invalidated
let s1 = String::from("moved text");
let s2 = s1;
println!("Move: s2 = '{s2}'");
// 3. Clone: explicit deep heap duplication; both `s2` and `s3` remain valid
let s3 = s2.clone();
println!("Clone: s2 = '{s2}', s3 = '{s3}'");
}
Copy: p1 = Point(3, 4), p2 = Point(3, 4)
Move: s2 = 'moved text'
Clone: s2 = 'moved text', s3 = 'moved text'
Choosing the Right Mechanism
- Default to borrowing (
&T) or moving (T) whenever a function or variable does not need a second independent heap copy. - Derive
Copy(#[derive(Clone, Copy)]) on small stack-only value types so they behave like primitive integers. - Call
.clone()explicitly when you intentionally need two independent owners of heap data (or useRc<T>/Arc<T>when.clone()should cheaply share read-only heap data via reference counting).