Guide contents

Lesson 12 of 94

Move, Copy & Clone

The previous lesson showed that assigning a String moves it, while assigning an integer copies it. This lesson makes the rule precise and shows the third option, cloning, which you use whenever you really want two independent values.

What happens on let b = a; a still usable? Cost
Move (String, Vec, most types) Ownership transfers to b No Copies a few bytes of header
Copy (i32, f64, bool, char, ...) Bits are duplicated Yes Copies a few bytes
Clone (let b = a.clone();) A full, independent duplicate is built Yes Allocates and copies all the data

Rust makes every copy of heap data explicit and visible in the code.

1. Which types are Copy

A type is Copy when duplicating its bits is a complete, safe copy, so there is no heap data involved. These are all Copy:

  • all integer types, all float types, bool, char
  • tuples and arrays whose elements are all Copy
  • shared references &T, covered in References & Borrowing
Rust
fn main() {
    let count = 10;
    let ratio = 0.5;
    let flag = true;
    let letter = 'z';
    let point = (3, 4);
    let rgb = [255, 128, 0];

    let (c2, r2, f2, l2, p2, rgb2) = (count, ratio, flag, letter, point, rgb);

    // every original is still usable
    println!("{count} {ratio} {flag} {letter} {:?} {:?}", point, rgb);
    println!("{c2} {r2} {f2} {l2} {:?} {:?}", p2, rgb2);
}
Output
10 0.5 true z (3, 4) [255, 128, 0]
10 0.5 true z (3, 4) [255, 128, 0]

2. A tuple is Copy only if every part is

Rust
fn main() {
    let dimensions = (1920, 1080, 60.0); // (i32, i32, f64): all Copy
    let saved = dimensions;
    println!("{:?} and {:?}", dimensions, saved);
}
Output
(1920, 1080, 60.0) and (1920, 1080, 60.0)

Add one String and the whole tuple moves instead:

Rust✗ does not compile · E0382
fn main() {
    let labeled = (1920, 1080, String::from("Full HD"));
    let saved = labeled;
    println!("{:?}", labeled);
}
Output
error[E0382]: borrow of moved value: `labeled`
 --> src/main.rs:4:22
  |
2 |     let labeled = (1920, 1080, String::from("Full HD"));
  |         ------- move occurs because `labeled` has type `(i32, i32, String)`, which does not implement the `Copy` trait
3 |     let saved = labeled;
  |                 ------- value moved here
4 |     println!("{:?}", labeled);
  |                      ^^^^^^^ value borrowed here after move
  |
help: consider cloning the value if the performance cost is acceptable
  |
3 |     let saved = labeled.clone();
  |                        ++++++++

3. An array is Copy only if its elements are

Rust✗ does not compile · E0382
fn main() {
    let numbers = [1, 2, 3];
    let numbers_copy = numbers;       // fine: [i32; 3] is Copy

    let names = [String::from("a"), String::from("b")];
    let names_moved = names;          // moves: [String; 2] is not Copy
    println!("{:?} {:?}", numbers, numbers_copy);
    println!("{:?}", names);
}
Output
error[E0382]: borrow of moved value: `names`
 --> src/main.rs:8:22
  |
5 |     let names = [String::from("a"), String::from("b")];
  |         ----- move occurs because `names` has type `[String; 2]`, which does not implement the `Copy` trait
6 |     let names_moved = names;          // moves: [String; 2] is not Copy
  |                       ----- value moved here
7 |     println!("{:?} {:?}", numbers, numbers_copy);
8 |     println!("{:?}", names);
  |                      ^^^^^ value borrowed here after move
  |
help: consider cloning the value if the performance cost is acceptable
  |
6 |     let names_moved = names.clone();          // moves: [String; 2] is not Copy
  |                            ++++++++

4. clone() makes a deep, independent copy

.clone() allocates new heap memory and copies the data into it. The two values are completely independent afterwards.

Rust
fn main() {
    let original = String::from("draft");
    let mut edited = original.clone();
    edited.push_str(" v2");
    println!("original: {original}");
    println!("edited:   {edited}");
}
Output
original: draft
edited:   draft v2
Text
 original ┌─────┐          heap
          │ ptr ├──────► "draft"
          └─────┘
 edited   ┌─────┐
          │ ptr ├──────► "draft v2"     (a separate allocation)
          └─────┘

5. Cloning a Vec

Rust
fn main() {
    let prices = vec![10, 20, 30];
    let mut discounted = prices.clone();
    for p in &mut discounted {
        *p -= 5;
    }
    println!("prices:     {:?}", prices);
    println!("discounted: {:?}", discounted);
}
Output
prices:     [10, 20, 30]
discounted: [5, 15, 25]

6. Cloning a Vec<String> clones every String

clone() is deep: cloning a vector of strings allocates a new vector and a new copy of every string in it.

Rust
fn main() {
    let team = vec![String::from("Ana"), String::from("Ben")];
    let mut backup = team.clone();
    backup[0].push_str(" (lead)");
    backup.push(String::from("Cy"));
    println!("team:   {:?}", team);
    println!("backup: {:?}", backup);
}
Output
team:   ["Ana", "Ben"]
backup: ["Ana (lead)", "Ben", "Cy"]

7. Fixing "moved into a Vec" with clone

When you need the value in a vector and afterwards, clone it before pushing.

Rust
fn main() {
    let mut audit_log = Vec::new();
    let username = String::from("root");
    audit_log.push(username.clone());
    println!("logged in as {username}");
    println!("audit log: {:?}", audit_log);
}
Output
logged in as root
audit log: ["root"]

8. Fixing "moved in a previous iteration" with clone

This is the fix for the loop example from the previous lesson: each iteration pushes its own copy.

Rust
fn main() {
    let report = String::from("quarterly numbers");
    let mut outbox = Vec::new();
    for _ in 0..3 {
        outbox.push(report.clone());
    }
    println!("{} copies, original still here: {report}", outbox.len());
}
Output
3 copies, original still here: quarterly numbers

9. Fixing "cannot move out of index" with clone

To get an owned copy of one element and leave the vector intact, clone that element.

Rust
fn main() {
    let names = vec![String::from("alpha"), String::from("beta")];
    let mut first = names[0].clone();
    first.push_str("-copy");
    println!("{first}");
    println!("{:?}", names);
}
Output
alpha-copy
["alpha", "beta"]

10. Copy elements come out of a Vec freely

Reading an element of a Copy type copies it, so there is no problem taking it "out" by index.

Rust
fn main() {
    let latencies = vec![120, 85, 300];
    let first = latencies[0];
    let worst = latencies[2];
    println!("first={first} worst={worst} all={:?}", latencies);
}
Output
first=120 worst=300 all=[120, 85, 300]

11. to_string() and to_owned() create owned copies of text

A string literal is borrowed text (&str). to_string(), to_owned(), and String::from all make an owned String copy of it. They do the same thing; pick one style.

Rust
fn main() {
    let literal = "config.toml";
    let a = literal.to_string();
    let b = literal.to_owned();
    let c = String::from(literal);
    println!("{a} {b} {c} {literal}");
}
Output
config.toml config.toml config.toml config.toml

12. A clone's capacity fits its length

A clone allocates just enough memory for the current contents. Spare capacity is not copied.

Rust
fn main() {
    let mut buffer = String::with_capacity(1024);
    buffer.push_str("small");
    let snapshot = buffer.clone();
    println!("buffer:   len={} capacity={}", buffer.len(), buffer.capacity());
    println!("snapshot: len={} capacity={}", snapshot.len(), snapshot.capacity());
}
Output
buffer:   len=5 capacity=1024
snapshot: len=5 capacity=5

String::with_capacity(1024) reserves space up front.

13. Copy is implicit, Clone is explicit

Every Copy type is also Clone, so calling .clone() on an integer works, but it is pointless: the value would have been copied anyway. Clippy warns about it.

Rust
fn main() {
    let a = 7;
    let b = a.clone(); // works, but just write `let b = a;`
    let c = a;
    println!("{a} {b} {c}");
}
Output
7 7 7

The deeper rule: a type can only be Copy if copying its bits is a complete copy. String cannot be Copy, because copying its bits would duplicate the pointer and lead to the double free described in Ownership.

14. Clones are independent in both directions

Rust
fn main() {
    let mut original = vec![1, 2, 3];
    let copy = original.clone();
    original.push(4);
    original[0] = 100;
    println!("original: {:?}", original);
    println!("copy:     {:?}", copy);
}
Output
original: [100, 2, 3, 4]
copy:     [1, 2, 3]

15. Clone to keep the original order while sorting

Rust
fn main() {
    let arrivals = vec![
        (38, String::from("Chen")),
        (52, String::from("Ada")),
        (41, String::from("Bo")),
    ];
    let mut by_score = arrivals.clone();
    by_score.sort();
    by_score.reverse();

    println!("in arrival order:");
    for (score, name) in &arrivals {
        println!("  {name}: {score}");
    }
    println!("leaderboard:");
    for (rank, (score, name)) in by_score.iter().enumerate() {
        println!("  #{} {name}: {score}", rank + 1);
    }
}
Output
in arrival order:
  Chen: 38
  Ada: 52
  Bo: 41
leaderboard:
  #1 Ada: 52
  #2 Bo: 41
  #3 Chen: 38

Tuples sort by their first element, then their second, so sorting (score, name) pairs sorts by score.

16. When a clone is a smell

Cloning just to satisfy the compiler is the most common beginner habit. It works, but it copies data for no reason:

Rust
fn main() {
    let title = String::from("Rust for Java Developers");
    let length = title.clone().len(); // copies the whole string just to measure it
    println!("{title} has {length} bytes");
}
Output
Rust for Java Developers has 24 bytes

title.len() only needs to look at the string, and looking never requires ownership. Rust's tool for looking without owning is the reference, and calling a method like title.len() already uses one behind the scenes:

Rust
fn main() {
    let title = String::from("Rust for Java Developers");
    let length = title.len(); // no clone needed
    println!("{title} has {length} bytes");
}
Output
Rust for Java Developers has 24 bytes

Rule of thumb: clone when you need a second, independent value that you will change or keep separately. When you only need to read, borrow instead. References & Borrowing covers this in depth.

17. Moves are free, clones are not

A move of a Vec with a million elements copies 24 bytes. A clone copies a million elements. This example measures the difference in memory.

Rust
fn main() {
    let big: Vec<u64> = vec![7; 1_000_000];
    let moved = big; // 24 bytes copied
    let cloned = moved.clone(); // 8 MB allocated and copied

    let bytes = cloned.len() * std::mem::size_of::<u64>();
    println!("each vector holds {} MB of data", bytes / 1_000_000);
    println!("a move copied {} bytes", std::mem::size_of::<Vec<u64>>());
}
Output
each vector holds 8 MB of data
a move copied 24 bytes

This is why Rust makes clones explicit: you can find every expensive copy by searching for .clone().

18. Cloning Option and tuple values

clone() works on compound values too, cloning every part.

Rust
fn main() {
    let pair = (String::from("key"), vec![1, 2]);
    let pair_copy = pair.clone();
    println!("{:?} {:?}", pair, pair_copy);

    let maybe_name = Some(String::from("Ferris"));
    let copy_of_name = maybe_name.clone();
    println!("{:?} {:?}", maybe_name, copy_of_name);
}
Output
("key", [1, 2]) ("key", [1, 2])
Some("Ferris") Some("Ferris")

19. A value moved out can be cloned first

When one piece of code needs to consume a value and you still need it afterwards, clone it right where it is consumed.

Rust
fn main() {
    let words = vec![String::from("stop"), String::from("go")];
    let mut total = 0;
    for w in words.clone() {  // the loop consumes the clone
        total += w.len();
    }
    println!("{total} bytes in {:?}", words);
}
Output
6 bytes in ["stop", "go"]

Here for w in &words would be better, because it avoids the clone entirely. The clone is shown so you can recognize the pattern in other code.

20. Realistic example: undo history with snapshots

An editor keeps a snapshot of the document before each edit. Each snapshot must be an independent copy, otherwise later edits would change the saved history. This is exactly what clone is for.

Rust
fn main() {
    let mut document = vec![String::from("# Title")];
    let mut history: Vec<Vec<String>> = Vec::new();

    history.push(document.clone());
    document.push(String::from("First paragraph."));

    history.push(document.clone());
    document.push(String::from("Second paragraph."));

    history.push(document.clone());
    document[0] = String::from("# Better Title");

    println!("current: {:?}", document);
    println!("undo once: {:?}", history[history.len() - 1]);
    println!("undo all:  {:?}", history[0]);
}
Output
current: ["# Better Title", "First paragraph.", "Second paragraph."]
undo once: ["# Title", "First paragraph.", "Second paragraph."]
undo all:  ["# Title"]

21. Realistic example: configuration templates

A base configuration is cloned for each environment and then customized. Every environment gets its own copy, and the base stays untouched.

Rust
fn main() {
    let base: Vec<(String, String)> = vec![
        (String::from("log_level"), String::from("info")),
        (String::from("pool_size"), String::from("10")),
        (String::from("region"), String::from("eu-west-1")),
    ];

    let mut dev = base.clone();
    dev[0].1 = String::from("debug");

    let mut prod = base.clone();
    prod[1].1 = String::from("50");
    prod.push((String::from("alerts"), String::from("pagerduty")));

    for (name, config) in [("base", &base), ("dev", &dev), ("prod", &prod)] {
        print!("{name:5}:");
        for (key, value) in config {
            print!(" {key}={value}");
        }
        println!();
    }
}
Output
base : log_level=info pool_size=10 region=eu-west-1
dev  : log_level=debug pool_size=10 region=eu-west-1
prod : log_level=info pool_size=50 region=eu-west-1 alerts=pagerduty

22. Realistic example: fan-out notifications

One alert is delivered to several channels, and each channel's queue owns its own copy of the message.

Rust
fn main() {
    let alert = format!("[CRITICAL] {} is down", "checkout-service");
    let mut email_queue = Vec::new();
    let mut sms_queue = Vec::new();
    let mut chat_queue = Vec::new();

    email_queue.push(alert.clone());
    sms_queue.push(alert.clone());
    chat_queue.push(alert); // the last one can take the original: no clone needed

    println!("email: {:?}", email_queue);
    println!("sms:   {:?}", sms_queue);
    println!("chat:  {:?}", chat_queue);
}
Output
email: ["[CRITICAL] checkout-service is down"]
sms:   ["[CRITICAL] checkout-service is down"]
chat:  ["[CRITICAL] checkout-service is down"]

Notice the last push moves the original instead of cloning it, which saves one allocation. Clippy points out a clone right before the last use of a value (redundant_clone). When many owners need the same large value without copying it, Rust has Rc and Arc, covered in Rc & Weak.

23. Realistic example: a shopping cart with a saved-for-later list

Moving and cloning together: moving an item between lists transfers it, and "buy again" clones it so it stays in the order history.

Rust
fn main() {
    let mut cart = vec![String::from("headphones"), String::from("cable"), String::from("case")];
    let mut saved_for_later = Vec::new();
    let mut order_history = vec![String::from("charger")];

    // move "cable" from the cart to saved-for-later: no copy
    let item = cart.remove(1);
    saved_for_later.push(item);

    // "buy again": copy from history into the cart, history keeps its entry
    cart.push(order_history[0].clone());

    // checkout: move everything from the cart into history
    for item in cart {
        order_history.push(item);
    }

    println!("saved for later: {:?}", saved_for_later);
    println!("order history:   {:?}", order_history);
}
Output
saved for later: ["cable"]
order history:   ["charger", "headphones", "case", "charger"]

After the checkout loop, cart has been consumed. Trying to print it would be an E0382 error, because every item now belongs to order_history.

24. Realistic example: building test fixtures

Tests often start from the same data and change one thing. Cloning a template keeps each case isolated.

Rust
fn is_valid_user(user: &(String, String, u32)) -> bool {
    let (name, email, age) = user;
    !name.is_empty() && email.contains('@') && *age >= 13
}

fn main() {
    let template = (String::from("sam"), String::from("sam@example.com"), 30);

    let mut no_name = template.clone();
    no_name.0 = String::new();

    let mut bad_email = template.clone();
    bad_email.1 = String::from("sam.example.com");

    let mut too_young = template.clone();
    too_young.2 = 9;

    println!("template:  {}", is_valid_user(&template));
    println!("no name:   {}", is_valid_user(&no_name));
    println!("bad email: {}", is_valid_user(&bad_email));
    println!("too young: {}", is_valid_user(&too_young));
}
Output
template:  true
no name:   false
bad email: false
too young: false

The function takes &(String, String, u32), a reference to the tuple, so it can check a user without taking ownership of it. The next two lessons explain these references and why *age is needed.

Summary

  • Copy types (numbers, bool, char, and tuples and arrays made only of them) are duplicated on assignment. Both variables stay valid.
  • Every other type moves on assignment. Only a small header is copied, and the old variable becomes invalid.
  • .clone() creates a deep, independent copy, allocating new heap memory. It is always explicit.
  • Clone when you need a second value that lives or changes independently. When you only need to read, borrow instead (next lessons).
  • Avoid a clone right before the last use of a value: move it instead.

Next: Ownership & Functions.