Beginner · Ownership: Rust's big idea · topic 12 of 50 · 25 min
Copy and Clone
Explanation
Why do numbers not move?
You may have noticed that this works fine: let a = 5; let b = a; println!("{a} {b}");. If a String moves, why does a number not move? Because an i32 lives entirely on the stack and has nothing on the heap to free. Copying its 4 bytes is as cheap as moving them, and there is no risk of a double free. So Rust simply copies it.
The Copy trait
A trait is a label that says a type has some ability (module 7 covers traits fully). Types with the Copy trait are copied on assignment instead of moved. The old variable stays valid.
| Copy (copied, old stays valid) | Not Copy (moved) |
|---|---|
| All integers: i32, u8, usize... | String |
| f32, f64 | Vec<T>, HashMap |
| bool, char | Box<T> |
| &T (shared references) | &mut T (mutable references) |
| Tuples and arrays whose parts are all Copy, like (i32, bool) or [u8; 4] | Tuples or arrays containing a String |
let x = 10;
let y = x; // copied
println!("{x} {y}"); // 10 10: x is still valid
let t = (1, 'a');
let u = t; // copied: both parts are Copy
println!("{:?} {:?}", t, u);
let p = (1, String::from("a"));
let q = p; // MOVED: one part is a String
// println!("{:?}", p); // error: borrow of moved valueClone: a deliberate deep copy
Sometimes you really need two independent Strings. Call .clone(). It allocates new heap memory and copies every byte. Each copy has its own owner and is dropped on its own.
let a = String::from("Ravi");
let b = a.clone();
STACK HEAP
a: ptr -------------> R a v i
b: ptr -------------> R a v i (a separate copy)Rust makes cloning visible on purpose. In some languages, copying a big list happens silently and slows a program without anyone noticing. In Rust, every expensive copy has .clone() written in the code, so you can find it and ask "is this needed?"
Copy versus Clone
| Copy | Clone | |
|---|---|---|
| How it happens | Automatically on assignment and calls | Only when you write .clone() |
| Cost | Tiny: a few bytes | Can be large: copies heap data |
| Which types | Simple stack-only types | Almost all types, including String and Vec |
Every Copy type is also Clone, so calling .clone() on an i32 works; it just does the same cheap copy.
Your own types
Structs you define (module 5) are moved by default. If all their fields are Copy and the type is small, you can make it Copy with a derive line. Derive asks the compiler to write the trait code for you.
#[derive(Debug, Clone, Copy)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let a = Point { x: 1, y: 2 };
let b = a; // copied
println!("{:?} {:?}", a, b);
}Common mistakes
- Sprinkling .clone() everywhere to silence the compiler. It works, but it can make code slow and hides a design problem. First try borrowing (module 4).
- Expecting a struct to be copied like a number. Structs move unless they derive Copy.
- Trying to derive Copy on a struct with a String field. The compiler refuses: String is not Copy.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Copy and Clone · 1/7
Why does a number not move like a String? Because it lives only on the stack and owns nothing on the heap.
Prefer a video by a teacher? Find free videos on “rust copy vs clone trait explained” on YouTube ↗
In real life: Saving a draft before editing
A note-taking app keeps the last saved version of a note so the user can press Undo. Before editing, it clones the String: one copy is kept as the backup, the other is changed. Both own their own memory. The note's word count, an integer, is simply copied wherever it is needed, because integers are Copy.
fn main() {
let mut note = String::from("Buy milk");
let backup = note.clone();
note.push_str(" and bread");
let words = note.split_whitespace().count();
let shown = words; // usize is Copy
println!("{note} | backup: {backup} | {words} {shown}");
}Practice (0/3 done)
Which of these compiles?
What does this print? let mut a = String::from("hi"); let b = a.clone(); a.push('!'); println!("{a} {b}");
Define a struct Rgb with three u8 fields and derive Debug, Clone and Copy. Copy one value into another variable and print both. Then add a name: String field and see which error the compiler gives for Copy. Remove Copy and use .clone() instead.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Ownership and functionsOwnership: Rust's big idea · same module
- Memory 101: the stack and the heapOwnership: Rust's big idea · same module
- Traits: shared behaviourModules, crates, traits and generics
- Trait objects and dynamic dispatchModules, crates, traits and generics
- Interview questions: ownership, borrowing and lifetimesRust careers and interviews
- Box<T>: values on the heapClosures, iterators and smart pointers
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
Which type is Copy?
Answer: char
char is a simple stack-only type, so it is Copy.
Is (i32, f64, bool) Copy?
Answer: Yes, because every part is Copy
A tuple is Copy when all of its parts are Copy.
What does .clone() on a String do?
Answer: Allocates new heap memory and copies all the bytes
clone makes a deep, independent copy.
Why should you not add .clone() everywhere?
Answer: Each clone can copy a lot of data and slow the program
Clones cost time and memory; borrowing is often the better fix.
Can a struct with a String field derive Copy?
Answer: No, because String is not Copy
Copy is only allowed when every field is Copy.
Can you implement std's Display for std's Vec<u8> in your crate?Related
Answer: No, because of the orphan rule
Neither the trait nor the type is yours. Wrap Vec in a newtype instead.
Which is a valid array type for five f64 values?Related
Why can Clone not be used as dyn Clone?Related
Answer: clone returns Self, so it is not dyn compatible
Methods returning Self break dyn compatibility.
Which parameter type is most flexible for read-only text?Related
Answer: &str
&str accepts literals, Strings and slices.
From Interview questions: ownership, borrowing and lifetimes
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 1 more topic in “Ownership: Rust's big idea” before its exam: Ownership and functions.
- Module 3 exam: Ownership · 12 questions · 20 min, without help. 4 topics of this module still to finish first.
- Then the next module, Borrowing, slices and lifetimes. 50 topics left in the course before the final exam.