title: Copy and Clone
Rust has two ways to duplicate a value: Copy (implicit, cheap) and Clone (explicit, potentially expensive). See also [[ownership]] for how move semantics work.
Pass by value has no hidden cost
When you pass a heap-allocated type like Vec to a function, the heap data doesn’t move. What actually gets passed is a small stack descriptor — a pointer, a length, and a capacity (24 bytes). The function takes ownership of that descriptor and becomes responsible for freeing the heap data when it goes out of scope.
let vec0 = vec![22, 44, 66];
let vec1 = fill_vec(vec0); // only 24 bytes copied, heap data stays in place
// vec0 is no longer valid — ownership transferredNo clone, no allocation — just a transfer of responsibility.
Copy
Copy types live entirely on the stack and are cheap to duplicate — integers, booleans, floats, chars, and tuples of those types.
When you pass a Copy type to a function or assign it to another variable, Rust silently copies the bits. The original binding stays valid.
let x = 5;
let y = x; // copied, not moved
println!("{x}"); // still validYou cannot implement Copy on a type that owns heap data. If two bindings held the same heap pointer, both would try to free it when they went out of scope — a double-free. Rust prevents this at the type system level.
Clone
Clone is an explicit deep copy. It duplicates the heap data, producing a fully independent value. You have to write .clone() — Rust will never do this silently.
let v1 = vec![1, 2, 3];
let v2 = v1.clone(); // heap data duplicated — two independent vecs
println!("{v1:?}"); // still valid, v1 wasn't moved
println!("{v2:?}");Clone can be expensive for large data structures. Writing it explicitly is a signal that you know you’re paying that cost.
Summary
Copy | Clone | |
|---|---|---|
| Triggered | Implicitly on assignment/pass | Explicitly with .clone() |
| Cost | Always cheap (stack only) | Potentially expensive (heap duplication) |
| Original binding | Still valid | Still valid |
| Heap types | Not allowed | Allowed |