Clone and Copy
Rust normally moves a value when you assign it to a new variable or pass it to a function, which means the original binding becomes invalid. But some values don’t move at all — they get silently duplicated instead. That distinction is controlled by two related traits, Copy and Clone, and understanding exactly when each applies is essential to reading and writing correct Rust code.
Overview: Why some values move and others copy
Recall the core ownership rule: every value has exactly one owner, and when you write let b = a; for a non-Copy type, ownership of the value moves from a to b. After that, using a is a compile error. This exists to prevent two variables from both believing they own — and are responsible for freeing — the same heap memory, which would cause a double free.
But think about a plain integer like let x: i32 = 5;. An i32 is just 4 bytes sitting on the stack. There is no heap allocation to worry about, no destructor to run twice, nothing dangerous about having two independent copies of those 4 bytes. So for types like this, Rust doesn’t bother moving — it just duplicates the bits. This behavior is enabled by the Copy marker trait. Types that implement Copy include all the integer types, floating-point types, bool, char, and tuples or fixed-size arrays made entirely of Copy types.
String is different. A String is really a small struct on the stack (a pointer, a length, and a capacity) pointing at a buffer it owns on the heap. If assignment merely copied those three fields, you’d end up with two String values pointing at the same heap buffer. When both go out of scope, Rust would try to free that buffer twice — undefined behavior. This is exactly why String, Vec<T>, HashMap, and most custom structs are not Copy: duplicating their bits would not be safe. Assigning one of these moves it instead.
So what if you genuinely want an independent, safe duplicate of a String or a Vec<T>, heap allocation and all? That’s what the Clone trait is for. Calling .clone() explicitly asks Rust to perform a deep copy: allocate new heap memory and copy the contents into it. Unlike the silent, free duplication that Copy types get, Clone is always an explicit method call — because it can be expensive (an O(n) heap allocation and copy for a large Vec), Rust never does it implicitly. If you see .clone() in Rust code, you immediately know: “a real copy is being made here, on purpose.”
The two traits are related: Copy is technically a sub-trait of Clone (every Copy type must also implement Clone), so you can always call .clone() on a Copy type too — it just ends up doing the same trivial bit-copy that assignment would have done anyway. The reverse isn’t true: implementing Clone does not make a type Copy. A type can only be Copy if every one of its fields is Copy, and it must not implement Drop (a type with custom cleanup logic can never be silently duplicated, since the compiler wouldn’t know how many times to run that cleanup).
Syntax
You rarely implement these traits by hand; instead you ask the compiler to generate them with #[derive(...)] attributes placed above a struct or enum definition.
#[derive(Clone)]
struct A { /* fields */ }
#[derive(Clone, Copy)]
struct B { /* fields, all of which must be Copy */ }
let original = value;
let duplicate = original.clone(); // explicit deep copy, works for any Clone type
| Element | Meaning |
|---|---|
#[derive(Clone)] |
Auto-generates a .clone() method that clones every field. Works for almost any type, including ones that own heap data. |
#[derive(Clone, Copy)] |
Also marks the type as Copy. Requires every field to already implement Copy, and the type must not implement Drop. Must always list both — Copy alone cannot be derived without Clone. |
.clone() |
Method from the Clone trait. Call it explicitly whenever you need an independent copy of a non-Copy value. |
Plain assignment (let b = a;) |
Duplicates automatically if the type is Copy; otherwise moves ownership from a to b. |
Examples
Example 1: Copy types duplicate silently
fn main() {
let x = 5;
let y = x; // i32 is Copy, so x is duplicated, not moved
println!("x = {}, y = {}", x, y);
let point1 = (3, 4);
let point2 = point1; // a tuple of Copy types is also Copy
println!("point1 = {:?}, point2 = {:?}", point1, point2);
}
Output:
x = 5, y = 5
point1 = (3, 4), point2 = (3, 4)
Both x and y remain valid after the assignment because i32 implements Copy — there was never a move here, just a cheap duplication of 4 bytes. The same applies to the tuple, since it’s made entirely of Copy elements.
Example 2: cloning a heap-owning type
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone(); // deep copy: new heap buffer, same contents
println!("s1 = {}, s2 = {}", s1, s2);
let s3 = s1; // this MOVES s1 into s3 (String is not Copy)
println!("s3 = {}", s3);
}
Output:
s1 = hello, s2 = hello
s3 = hello
s1.clone() allocates a brand-new heap buffer for s2 and copies "hello" into it, so s1 and s2 are two fully independent strings. That clone doesn’t consume s1 — cloning only ever borrows &self — so s1 is still perfectly usable afterward. The later line let s3 = s1; is a plain move, not a clone: no new heap memory is allocated, ownership of the existing buffer just transfers to s3, and s1 becomes invalid from that point on.
Example 3: deriving Copy vs deriving only Clone on structs
#[derive(Debug, Clone, Copy)]
struct Point {
x: i32,
y: i32,
}
#[derive(Debug, Clone)]
struct Wrapper {
label: String,
values: Vec<i32>,
}
fn main() {
let p1 = Point { x: 1, y: 2 };
let p2 = p1; // Point is Copy, so p1 is still valid
println!("p1 = {:?}, p2 = {:?}", p1, p2);
let w1 = Wrapper {
label: String::from("data"),
values: vec![1, 2, 3],
};
let w2 = w1.clone(); // Wrapper is not Copy, so we clone explicitly
println!("w1 = {:?}", w1);
println!("w2 = {:?}", w2);
}
Output:
p1 = Point { x: 1, y: 2 }, p2 = Point { x: 1, y: 2 }
w1 = Wrapper { label: "data", values: [1, 2, 3] }
w2 = Wrapper { label: "data", values: [1, 2, 3] }
Point only contains two i32 fields, both Copy, so the compiler is allowed to derive Copy for it — assigning p1 to p2 duplicates it silently and both remain usable. Wrapper contains a String and a Vec<i32>, neither of which is Copy, so Wrapper can only derive Clone. Calling w1.clone() deep-copies both the string buffer and the vector’s backing array, producing a fully independent w2.
How it works step by step
- When the compiler sees
let b = a;or a value passed by-value into a function, it checks whether the value’s type implementsCopy. - If it does, the compiler emits a simple bitwise duplication (like a
memcpyof the value’s stack representation). Bothaandbremain valid, independent bindings. No heap interaction happens for aCopytype, becauseCopytypes never own heap memory. - If the type does not implement
Copy, the compiler instead performs a move: it transfers logical ownership to the new binding and marks the old one as no longer usable. No bytes are copied at the machine-code level for the heap data itself — only the small stack-resident pointer/length/capacity fields are transferred, and the borrow checker statically forbids any further use of the old binding. - When you call
.clone(), the compiler instead runs the type’s actualClone::cloneimplementation, which for derived types recursively clones every field. For aStringorVec<T>, this allocates a new heap buffer and copies the elements into it — a real, potentially expensive runtime operation, unlike a move or aCopyduplication. - Because
CopyrequiresCloneas a supertrait, everyCopytype also has a working.clone()— it just happens to be exactly as cheap as the implicit duplication would have been.
Common Mistakes
Mistake 1: using a value after it moved, assuming it would be copied
Beginners often expect a Vec or String to behave like an i32 and remain usable after assignment. It doesn’t, because neither type implements Copy:
fn main() {
let v1 = vec![1, 2, 3];
let v2 = v1; // moves v1, does NOT copy it
println!("{:?}", v1); // error: value borrowed here after move
println!("{:?}", v2);
}
The compiler rejects this with a “value borrowed here after move” error, because v1‘s ownership was transferred to v2 on the previous line. The fix is to decide whether you actually need two independent vectors (call .clone()) or only needed v2 in the first place:
fn main() {
let v1 = vec![1, 2, 3];
let v2 = v1.clone(); // explicit deep copy of the Vec
println!("{:?}", v1);
println!("{:?}", v2);
}
Output:
[1, 2, 3]
[1, 2, 3]
Mistake 2: deriving Copy on a struct that contains a non-Copy field
#[derive(Copy, Clone)]
struct Item {
name: String, // String is not Copy
quantity: u32,
}
fn main() {
let item = Item { name: String::from("Widget"), quantity: 3 };
println!("{}", item.name);
}
This fails to compile: the trait Copy cannot be implemented for Item because its name field is a String, which is not Copy. The compiler enforces this because allowing it would let two Item values silently share (and later double-free) the same heap buffer. The fix is to derive only Clone, and call .clone() explicitly wherever you need a duplicate:
#[derive(Clone)]
struct Item {
name: String,
quantity: u32,
}
fn main() {
let item1 = Item { name: String::from("Widget"), quantity: 3 };
let item2 = item1.clone();
println!("{} x{}", item1.name, item1.quantity);
println!("{} x{}", item2.name, item2.quantity);
}
Output:
Widget x3
Widget x3
Mistake 3: cloning when a borrow would have worked
Once .clone() is available, it’s tempting to sprinkle it everywhere to make the borrow checker happy, even where a simple reference would do:
fn print_all(items: &Vec<String>) {
for item in items {
let owned = item.clone(); // unnecessary heap allocation just to read a value
println!("{}", owned);
}
}
fn main() {
let items = vec![String::from("a"), String::from("b")];
print_all(&items);
}
Output:
a
b
This compiles fine, but it’s wasteful: each iteration allocates and immediately throws away a new String just to print it, when the loop only ever needed to read the value. Since item is already a &String here, printing it directly works without any allocation:
fn print_all(items: &Vec<String>) {
for item in items {
println!("{}", item); // a borrow is enough, no clone needed
}
}
fn main() {
let items = vec![String::from("a"), String::from("b")];
print_all(&items);
}
Output:
a
b
Best Practices
- Derive
Copy(along withClone) for small, plain-data structs made entirely ofCopyfields, such as coordinate pairs or simple flags — it makes them much more pleasant to use since you never have to think about moves for them. - Never derive
Copyjust to silence a move error without thinking — if a struct owns aString,Vec<T>, or similar, it genuinely can’t beCopy, and the fix is almost always to borrow (&) instead of duplicating. - Reach for
.clone()only when you actually need two independent, owned copies that can be mutated or dropped separately. If you only need to read a value, pass a reference instead. - Treat a proliferation of
.clone()calls in your code as a signal to double-check your borrowing — it often means a function could take&Tinstead ofT. - Remember that
CopyandDropare mutually exclusive: if a type needs custom cleanup logic, it can never be markedCopy, by design. - When in doubt about whether an assignment moved or copied a value, try to use the original binding afterward — the compiler will tell you immediately if it was moved.
Practice Exercises
- Write a struct
Colorwith threeu8fields (r,g,b). Derive whatever traits let you assign oneColorto another and still use both afterward, then print both with{:?}(you’ll also need to deriveDebug). - Write a struct
Playlistcontaining aname: Stringand asongs: Vec<String>. Create onePlaylist, make an independent duplicate of it, add a song to only the duplicate, and print both to confirm the original is unaffected. - Write a function that takes a
&strand returns its length asusizewithout taking ownership of the caller’s string, then call it twice on the sameStringto prove no move or clone was necessary.
Summary
Copytypes (integers, floats,bool,char, and tuples/arrays of these) are duplicated automatically and cheaply on assignment or when passed by value; the original binding stays valid.- Types that own heap memory, like
StringandVec<T>, cannot beCopybecause silently duplicating their bits would risk a double free — assigning them moves ownership instead. Cloneprovides an explicit, possibly expensive, deep-copy operation via.clone(), available for almost any type including heap-owning ones.Copyis a supertrait ofClone: everyCopytype is alsoClone, but not the reverse.- A struct can only derive
Copyif every field isCopyand the struct doesn’t implementDrop. - Prefer borrowing over cloning when you only need to read data; reserve
.clone()for when you truly need an independent, owned duplicate.
