Skip to content

Beginner · Ownership: Rust's big idea · topic 12 of 50 · 25 min

Copy and Clone

Explanation

Small, simple types that live only on the stack (integers, floats, bool, char, and tuples or arrays of them) implement Copy: assignment copies them and the old variable stays valid. Types that own heap data, like String and Vec, are moved instead. .clone() makes a full, deep copy on purpose, which costs time and memory.

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, f64Vec<T>, HashMap
bool, charBox<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
rust
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 value
A tuple is Copy only if every part is Copy.

Clone: 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.

output
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)
After clone, a and b own different heap memory, so both are valid.

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

CopyClone
How it happensAutomatically on assignment and callsOnly when you write .clone()
CostTiny: a few bytesCan be large: copies heap data
Which typesSimple stack-only typesAlmost 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.

rust
#[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);
}
Copy requires Clone too, so both are derived. A struct with a String field cannot be Copy.

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.
Tip: Cloning is fine when the data is small or when you truly need an independent copy, for example to keep the original while changing a copy. Do not feel guilty about it in learning code; just know its cost.

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

i32: 4 bytes, no heap

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.

rust
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}");
}
Prints: Buy milk and bread | backup: Buy milk | 4 4

Practice (0/3 done)

Task 1 · Question

Which of these compiles?

Task 2 · Question

What does this print? let mut a = String::from("hi"); let b = a.clone(); a.push('!'); println!("{a} {b}");

Task 3 · Practice

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

Question 1

1.Which type is Copy?

Question 2

2.Is (i32, f64, bool) Copy?

Question 3

3.What does .clone() on a String do?

Question 4

4.Why should you not add .clone() everywhere?

Question 5

5.Can a struct with a String field derive Copy?

Read more (free)

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.

From Traits: shared behaviour

Which is a valid array type for five f64 values?Related

Answer: [f64; 5]

Array types are written [type; length].

From Compound types: tuples and arrays

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.

From Trait objects and dynamic dispatch

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. 1 more topic in “Ownership: Rust's big idea” before its exam: Ownership and functions.
  2. Module 3 exam: Ownership · 12 questions · 20 min, without help. 4 topics of this module still to finish first.
  3. Then the next module, Borrowing, slices and lifetimes. 50 topics left in the course before the final exam.

← Previous: The ownership rules and moves