Copy, Clone and explicit duplication
In Rust a move is the default for every type, and two traits are the exceptions. Copy marks a type small enough that duplicating it on assignment is cheaper than invalidating the old name, and Clone is a method that duplicates a value when the programmer asks for it. The difference is not stylistic: one happens implicitly and must be free, the other is written out and may allocate.
The previous lesson left a value moved and a compiler error behind. This one shows the two ways out: a type that copies itself, and a call to clone that says in the source that a heap buffer is being duplicated. Both programs are short, and the last one shows the error for a type that has neither.
Copy types
Integers, floats, bool, char, references and tuples of Copy types implement Copy. Assignment duplicates the bits and both bindings stay usable, which is why the first example looks like C++ and costs the same.
The program copies an integer and a boolean and prints all four names. Nothing is moved, so nothing is invalidated, and the compiler emits the same machine code it would for a plain assignment.
fn main() {
let a = 5;
let b = a;
let flag = true;
let other = flag;
println!("{a} {b} {flag} {other}");
}
rustc --edition 2024 copy.rs
./copy
5 5 true true
A type can be Copy only if all of its fields are Copy, which rules out String, Vec and anything that owns a buffer. That restriction is the language keeping the promise that a copy is cheap.
A struct that is Copy
#[derive(Copy, Clone)] makes a struct of Copy fields Copy as well. The attribute writes both implementations, because Copy requires Clone: a type that can be duplicated implicitly can also be cloned explicitly.
The struct below has two integers, so the derive is allowed. After let b = a; both values are readable, and the four numbers are the same two pairs printed twice.
#[derive(Copy, Clone)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let a = Point { x: 3, y: 4 };
let b = a;
println!("{} {} {} {}", a.x, a.y, b.x, b.y);
}
rustc --edition 2024 copy_struct.rs
./copy_struct
3 4 3 4
Adding a String field would make the derive fail with E0204, which says that the struct cannot implement Copy because one of its fields does not. The lesson on structs comes back to the attribute when it builds a value type.
Clone on purpose
String is Clone but not Copy. original.clone() allocates a second buffer, copies the bytes, and returns a value that owns its copy; both strings are then valid and independent. The word clone in the source is the signal that something was duplicated, and a reviewer can search for it.
The program below clones a string and compares the lengths. The comparison is true because the clone has the same five bytes, not because the two bindings share anything: changing one afterwards would leave the other alone.
fn main() {
let original = String::from("hello");
let copy = original.clone();
println!("{original} {copy}");
println!("same length: {}", original.len() == copy.len());
}
rustc --edition 2024 clone.rs
./clone
hello hello
same length: true
Cloning a String costs one allocation and one copy of the bytes, which is cheap for a short string and expensive in a loop over a large one. The lesson on String shows the methods that grow a buffer without copying it.
What a clone costs
A clone inside a loop is a clone per iteration. The program clones a four-byte string a thousand times and adds the lengths, so the total is four thousand: a thousand allocations and a thousand copies, each one invisible in the source unless the word clone is read.
The alternative is to borrow, which the next part of the course teaches. Until then, the number is the honest way to see the cost: a clone is not free, and the compiler will not do it behind your back.
fn main() {
let text = String::from("rust");
let mut total = 0;
for _ in 0..1000 {
let copy = text.clone();
total += copy.len();
}
println!("total: {total}");
}
rustc --edition 2024 clone_cost.rs
./clone_cost
total: 4000
A moved value would have cost nothing, and a borrowed one nothing either. The clone is the one option that allocates, which is why it is written out and why the compiler suggests it only when the alternative is a rejected program.
A type without Clone
A struct gets neither trait for free. Calling .clone() on one that has no implementation is E0599: the method does not exist for this type, and the help names the trait that would provide it. The fix is the derive, or a hand-written implementation when the struct owns something that needs a deep copy.
The program below defines a struct with a String field and no attribute. The error names the struct, the method and the candidate trait, in that order.
struct Config {
name: String,
}
fn main() {
let config = Config {
name: String::from("dev"),
};
let copy = config.clone();
println!("{}", copy.name);
}
rustc --edition 2024 not_clone_err.rs
error[E0599]: no method named `clone` found for struct `Config` in the current scope
--> not_clone_err.rs:9:23
|
1 | struct Config {
| ------------- method `clone` not found for this struct
...
9 | let copy = config.clone();
| ^^^^^ method not found in `Config`
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `clone`, perhaps you need to implement it:
candidate #1: `Clone`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0599`.
Adding #[derive(Clone)] to the struct is enough, because the derive clones each field and String is Clone. The lesson on derive and marker traits lists the other attributes that work the same way.
Exercises
- Add a String field to the Copy struct and read the error. Which trait does the compiler name?
- Change the clone-cost loop to clone a thousand-byte string and predict the total before running it.
- Give the struct in the last section the derive and run the program again. What does it print, and how many allocations did the clone make?
Questions
A new set of questions in a new order every time. Choose one answer for each, then check.
-
What is the difference between Copy and Clone?
Answer
Copy happens on assignment and must be cheap; Clone is a method that can allocate
Copy marks types duplicated implicitly by assignment, and a Copy type may not own a buffer.
-
Which types can implement Copy?
Answer
Only types whose fields are all Copy
Copy requires every field to be Copy, which keeps an implicit copy free of allocations.
-
What does
#[derive(Copy, Clone)]add to a struct?Answer
Both trait implementations, because Copy requires Clone
The derive writes both impls, and the compiler rejects Copy without Clone.
-
What does a clone of a String allocate?
Answer
One new buffer with a copy of the bytes
The clone owns its own buffer, so the bytes are copied once into a new allocation.
-
What does this program print?
fn main() { let a = 5; let b = a; let flag = true; let other = flag; println!("{a} {b} {flag} {other}"); }Answer
5 5 true true
Integers and booleans are Copy, so every name keeps its value.
-
What does this program print?
#[derive(Copy, Clone)] struct Point { x: i32, y: i32, } fn main() { let a = Point { x: 3, y: 4 }; let b = a; println!("{} {} {} {}", a.x, a.y, b.x, b.y); }Answer
3 4 3 4
A Copy struct duplicates on assignment, so both bindings hold the same two fields.
-
What does this program print?
fn main() { let original = String::from("hello"); let copy = original.clone(); println!("{original} {copy}"); }Answer
hello hello
The clone owns a second buffer, so both bindings print their own copy of the text.
-
What does this program print?
fn main() { let text = String::from("rust"); let mut total = 0; for _ in 0..4 { let copy = text.clone(); total += copy.len(); } println!("{total}"); }Answer
16
Each iteration clones four bytes, so four iterations add up to sixteen.
-
What is the first line of the compiler output for this program?
struct Config { name: String, } fn main() { let config = Config { name: String::from("dev"), }; let copy = config.clone(); println!("{}", copy.name); }Answer
error[E0599]: no method named
clonefound for structConfigin the current scopeConfig has no Clone implementation, and the help names Clone as the trait that would provide the method.
-
What does this program print?
fn main() { let value = 10; let copy = value; let text = String::from("hi"); let stolen = text; println!("{value} {copy} {stolen}"); }Answer
10 10 hi
The integer is Copy and stays usable, while the string moves to stolen and is still printed there.
The code on this page was built with rustc 1.96.1 and run on Alpine Linux 3.24.3 (x86_64) in a Docker container.
Article author: Arthur Isaev