When moving isn't enough
You write a function that takes a String. You call it, then try to print the original variable. The compiler stops you with E0382 (use of moved value). You moved the value. The original is gone. You didn't want to lose it. You wanted a copy.
Rust's default behavior is to move values. Assignment transfers ownership. If you want two independent values, you have to ask for a duplicate. That's what the Clone trait does. It creates a deep copy of a value. The new value lives in its own memory. Changing one doesn't affect the other.
Deep copy, not shallow copy
In Python or JavaScript, assigning a variable often copies a reference. You get two names pointing to the same object. Change one, and the other changes too. Rust doesn't do that by default. Rust moves. If you want duplication, you must be explicit.
Clone performs a deep copy. Every byte gets copied. If the value owns heap data, Clone allocates new heap memory and copies the data there. The result is a completely independent value. The original stays exactly where it was.
Think of a physical document. Moving is handing the paper to someone else. You no longer have it. Cloning is photocopying the document. You keep the original. The other person gets a separate copy. If they scribble on their copy, yours stays clean.
Minimal example
fn main() {
// Create a String. The data lives on the heap.
let s1 = String::from("hello");
// Clone allocates new heap memory and copies the bytes.
// s1 is untouched. s2 points to a different allocation.
let s2 = s1.clone();
// Both are valid. s1 wasn't moved.
println!("s1 = {s1}, s2 = {s2}");
// Modifying s2 does not affect s1.
s2.push_str(" world");
println!("s1 = {s1}, s2 = {s2}");
}
What happens under the hood
When you call clone(), Rust allocates new memory on the heap. It copies the bytes from the original value to the new location. It returns a new struct pointing to that new memory. The original struct stays exactly where it was.
For a String, this means a new heap allocation for the characters. For a Vec<T>, it means a new heap allocation for the vector buffer, and then cloning each element inside. If the elements are String, each element gets its own heap allocation too. Cloning is recursive. The cost adds up quickly for nested structures.
Cloning is not free. It costs CPU cycles to copy bytes. It costs memory to hold the duplicate. It triggers heap allocations, which can fragment memory and slow down your program. Use Clone when you actually need an independent copy. Don't clone just to make the compiler happy if a reference would work.
Deriving Clone for your types
Most types implement Clone automatically via the #[derive(Clone)] macro. The macro generates code that clones each field. If a field is a String, it calls String::clone. If a field is an i32, it copies the bits. The generated code handles the recursion.
// Derive Clone to get the implementation automatically.
// The compiler generates code that clones each field.
#[derive(Clone)]
struct Config {
query: String,
file_path: String,
timeout: u32,
}
fn parse_args(args: &[String]) -> Config {
// Clone strings from the slice into the struct.
// The slice borrows; the struct needs ownership.
let query = args[1].clone();
let file_path = args[2].clone();
// u32 implements Copy, so it copies automatically.
// No clone call needed for timeout.
let timeout = 30;
Config { query, file_path, timeout }
}
Custom Clone implementations are rare. You only need one if you have a type with special invariants that the derived code can't handle, or if you want to optimize the cloning process. For 99% of cases, #[derive(Clone)] is the right choice.
Clone versus Copy
Copy is a subtrait of Clone. Types that implement Copy also implement Clone, but they behave differently. Copy types duplicate automatically on assignment. You don't call .clone(). The compiler copies the bits for you.
Copy is reserved for small, stack-only types where copying is trivial. Integers, booleans, and pointers implement Copy. A String does not implement Copy because it owns heap data. Copying a String requires allocation, which is too expensive to happen silently on every assignment.
If a type implements Copy, you can still call .clone() on it. The compiler allows it, but it's redundant. The convention is to rely on assignment for Copy types and call .clone() only for non-Copy types. This signals to readers that the operation might be expensive.
Convention aside: Rc::clone
Rc<T> implements Clone. Calling .clone() on an Rc does not deep copy the inner data. It bumps a reference counter and returns a new Rc pointing to the same allocation. This is cheap.
The community convention is to write Rc::clone(&rc) instead of rc.clone(). Both compile and do the same thing. The explicit form signals that this is a reference-count bump, not a deep copy. rc.clone() looks like it might duplicate the data, which can mislead readers. Use the explicit form to avoid confusion.
Pitfalls and compiler errors
If you try to clone a type that doesn't implement Clone, the compiler rejects you with E0277 (trait bound not satisfied). Some types cannot be cloned. MutexGuard cannot be cloned. File handles cannot be cloned. Network sockets cannot be cloned. These types represent unique resources. Duplicating them would break safety invariants.
use std::sync::MutexGuard;
fn try_clone_guard(guard: MutexGuard<Vec<i32>>) {
// This fails with E0277.
// MutexGuard cannot be cloned because it represents
// exclusive access to a resource.
let _other = guard.clone();
}
The compiler protects you from duplicating unique resources. If a type doesn't implement Clone, it's guarding something that must remain unique. Respect that constraint. Work around it by cloning the data inside the resource, or by using references.
Another pitfall is cloning when you don't need to. Borrowing is cheaper. If a function takes &String, pass a reference. Don't clone the string just to satisfy the signature. Cloning adds allocation overhead and memory pressure. Profile your code. If cloning is a bottleneck, refactor to use references or shared ownership types.
Generics and Clone bounds
In generic code, you often need to require Clone. You add a trait bound to ensure the type can be duplicated. Without the bound, the compiler won't let you call .clone() on a generic parameter.
// Require Clone so we can duplicate the value.
fn duplicate<T: Clone>(value: T) -> (T, T) {
// Create a second copy.
let copy = value.clone();
(value, copy)
}
If you omit the bound, the compiler rejects the code with E0599 (no function or associated item named clone found for type parameter T). The bound tells the compiler that T supports cloning. It's a contract between the function and the caller.
Decision matrix
Use Clone when you need a second, independent value and the original must remain usable. Use Clone when passing data to a function that takes ownership but you need to keep the original alive. Use Copy for small, stack-only types like integers and booleans where the compiler can duplicate the bits automatically. Reach for references when you only need to read the data and don't need ownership. Reach for Rc<T> or Arc<T> when multiple parts of your program need to share the same data and you want to avoid the cost of deep copying.
Cloning is a tax on memory. Pay it only when you need independence. Borrowing is free. Cloning costs allocation and copy time. Pick the cheapest tool that solves the problem.