Rust Copy and Clone
Assigning one variable to another behaves differently for different types in Rust. Small values such as numbers get duplicated automatically. Heap-based values such as String move to the new owner instead. Two traits control this behavior: Copy and Clone.
Copy Types
A type that implements Copy duplicates its bits on assignment. Both variables stay usable, because each holds its own independent value.
let a = 5;
let b = a;
println!("{a} {b}");
STACK +-------+ | a = 5 | +-------+ | b = 5 | (a separate copy) +-------+
Types That Implement Copy
- All integer and float types
- bool and char
- Shared references such as
&str - Tuples and arrays that contain only Copy types
The copy costs almost nothing, because these values are tiny and live on the stack.
Move Behavior
A String owns heap memory. Copying only the handle would leave two owners for one block of letters, and both would try to free it. Rust prevents the problem by moving ownership. The old variable becomes invalid.
let s1 = String::from("tea");
let s2 = s1;
// println!("{s1}"); // error: value moved
Before move After move
s1 --> "tea" s1 (invalid)
s2 --> "tea"
Clone for Deep Copies
The clone method creates a full duplicate, including the heap data. Both variables own separate memory afterward.
let s1 = String::from("tea");
let s2 = s1.clone();
println!("{s1} and {s2}");
s1 --> "tea" s2 --> "tea" (second block of heap memory)
Clone calls are explicit on purpose. Seeing .clone() in code warns the reader that work and memory are being spent.
Passing Values to Functions
Function calls follow the same rules as assignment. A Copy value duplicates into the parameter. A String moves into the function.
fn show_number(n: i32) {
println!("{n}");
}
fn show_text(t: String) {
println!("{t}");
}
fn main() {
let n = 7;
show_number(n);
println!("{n}"); // works
let t = String::from("hello");
show_text(t.clone()); // clone keeps t alive
println!("{t}"); // works
}Borrowing with a reference avoids both moves and clones. Prefer borrowing when the function only needs to read the data.
Deriving Copy and Clone for Your Types
Custom structs gain these abilities with a derive attribute. Every field must support the trait you derive.
#[derive(Debug, Clone, Copy)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p1 = Point { x: 1, y: 2 };
let p2 = p1;
println!("{:?} {:?}", p1, p2);
}
Point { x: 1, y: 2 } Point { x: 1, y: 2 }
A struct holding a String can derive Clone but never Copy. The String field owns heap memory, which blocks bitwise duplication.
#[derive(Debug, Clone)]
struct User {
name: String,
age: u32,
}Choosing Between Them
| Question | Copy | Clone |
|---|---|---|
| Runs automatically? | Yes | No, call .clone() |
| Cost | Tiny | Can be large |
| Handles heap data? | No | Yes |
| Original stays usable? | Yes | Yes |
Performance Advice
Cloning large vectors inside loops slows programs quickly. Profile before optimizing, yet watch for clones added only to silence compiler errors. A borrow or a restructured function often solves the error without extra memory.
Common Mistakes
Calling clone() everywhere to silence compiler errors hides a design problem. Each clone allocates new heap memory. Ask whether a borrow can serve the same purpose.
Using a variable after moving it produces the well-known error message "borrow of moved value". Read the message carefully, because the compiler points to the exact line where ownership left the variable.
Assuming every small type implements Copy causes trouble as well. A struct becomes Copy only after you derive the trait, and only when every field is Copy.
Practice Ideas
- Write a function that takes a
Stringby value and observe the compiler error when you reuse the original. - Change the same function to take
&Stringand confirm the error disappears. - Create a
Pointstruct with Copy and aPersonstruct with Clone only, then compare how each assignment behaves.
Key Points
- Copy types duplicate automatically on assignment.
- Heap-owning types move by default.
- Call
clone()for an explicit deep copy. - Use
#[derive(Clone, Copy)]only when every field allows it.
