Skip to content

Clone and Copy

Clone is an explicit, visible copy that can be expensive; Copy is an implicit bitwise copy for small types that own no heap memory.

In Java, assignment copies primitives and copies references. Objects are never copied unless you write code to do it: a copy constructor, new ArrayList<>(list), or (rarely) clone(). So in Java, sharing is the default, and copies are explicit and somewhat rare.

Java

int count = 3;
int sameCount = count; // primitives are copied
var invoice = new Invoice("INV-7", new Money(1_999), new ArrayList<>(List.of(1_999L)));
var alias = invoice; // copies the reference only: one object, two names
var copy = new Invoice(invoice.number(), invoice.total(), new ArrayList<>(invoice.lines())); // a copy, by hand

Rust

let price = Money { cents: 1_999 };
let same_price = price; // `Copy`: a bitwise copy; `price` is still usable
let invoice = Invoice {
number: "INV-7".to_string(),
total: price,
};
let draft = invoice.clone(); // `Clone`: explicit; copies the `String` too

In Rust, assignment moves a value (see Ownership and moves). Two names cannot own the same value. If you need two independent values, you ask for a copy, and the copy is visible in the code: .clone().

There are two traits:

Copy Clone
How Implicit. let b = a; copies. Explicit. let b = a.clone();
Cost Always cheap: a copy of a few bytes. Anything from cheap to very expensive.
Which types Integers, floats, bool, char, &T, and small structs of Copy fields that derive it. Almost every type. String, Vec<T>, HashMap, and most structs.
Java analogy Primitives, or a C# struct. A copy constructor or a deep copy.

A type gets these traits with #[derive(...)]:

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Money {
pub cents: i64,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Invoice {
pub number: String, // a `String` is not `Copy`, so `Invoice` cannot be
pub total: Money,
}
  1. Line 1: Copy requires Clone, so both are in the list. Money has only an i64, so it can be Copy. PartialOrd and Ord add comparison.
  2. Line 6: Invoice can be Clone but not Copy, because it has a String field.
  3. Line 8: A String owns heap memory. Copying it means allocating and copying the text, so Rust makes you write .clone().

The important difference from Java: every expensive copy in Rust is visible. If you do not see .clone(), .to_vec(), .to_string(), .to_owned() or a similar call, no heap data was copied. This makes copies easy to find in a review, and it is why a long list of .clone() calls is worth a second look.

When you see .clone(), ask what type is cloned. The cost depends on the type, not on the method name.

Rc<T> and Arc<T> are reference-counted pointers (see Smart pointers). Cloning them does not copy T. It adds one to a counter, and returns another pointer to the same value. This is the closest thing in Rust to a Java reference:

pub fn share_catalog(catalog: &Rc<Vec<String>>) -> (Rc<Vec<String>>, usize) {
let for_report = Rc::clone(catalog); // cheap: +1 to the count, no copy
let owners = Rc::strong_count(catalog); // now 2 (or more)
(for_report, owners)
}

Many codebases write Rc::clone(&catalog) instead of catalog.clone(). Both do the same thing. The long form tells the reader “this is a cheap reference-count clone, not a deep copy”. Clippy can enforce it (clone_on_ref_ptr, off by default).

  • Rc and Arc clones: cheap, and the normal way to share.
  • Small data: a short String that becomes part of a new value, such as the customer name on an invoice.
  • The API needs an owned value: a struct field, a value sent to another thread, a value inserted into a map.
  • You need two versions: the original and a modified copy.
  • To fix a borrow or move error. The agent gets E0382 or E0502 and adds .clone(). The error goes away, and the reason for it stays. See S01: clone() to satisfy the borrow checker.
  • In a loop, especially on the collection that the loop iterates over.
  • On a large collection that is only read after the copy.
  • Chained: .clone().clone(), or .iter().map(|x| x.clone()) followed by code that only reads a field.

Cow<'_, str> (“clone on write”) holds either a borrowed value or an owned one. A function returns Cow when it usually returns its input unchanged, but sometimes must build a new value:

/// Returns the input unchanged (borrowed) when it has no tabs, and an
/// owned copy only when it must change it.
pub fn normalize_tabs(line: &str) -> Cow<'_, str> {
if line.contains('\t') {
Cow::Owned(line.replace('\t', " "))
} else {
Cow::Borrowed(line)
}
}

You do not need to write Cow often. When you read it, think “probably borrowed, owned only when necessary”. The caller can use it as a &str in both cases.

  • Each clone has a reason that you can say in one short sentence: “the invoice owns its customer name”, “the worker thread needs its own copy”.
  • Borrow first. Functions take &T or &[T] and clone only the parts that they must own.
  • Copy on small value types. IDs, money amounts, coordinates and small enums derive Copy (and Clone, Debug, PartialEq, Eq). Then they behave like Java primitives.
  • No Copy on types that might grow. Adding a String field to a Copy type later is a breaking change, because callers rely on the implicit copy.
  • Rc::clone(&x) or Arc::clone(&x) where the codebase wants cheap clones to look different from deep ones.
  • .clone() right before a value moves, added to fix E0382. Check if the function that takes the value could borrow it. See S01: clone() to satisfy the borrow checker.
  • for item in list.clone(): copies the whole list, often to avoid a borrow conflict in the loop body.
  • .iter().map(|x| x.clone()) or .cloned() followed by code that only reads one field.
  • Owned parameters that force callers to clone. fn f(name: String) when f only reads. See S05: Owned parameters (String, Vec<T>) where a borrow is sufficient.
  • A #[derive(Clone)] on a large type, and clones of it everywhere. Look for the design problem: perhaps the type should be shared with Arc, or the functions should borrow.

The task: from a list of orders, get the customers with many order lines, and all order IDs.

  1. WorseClones the whole list to iterate over it, clones each customer name twice, and clones every order to read its ID.

    pub fn customers_with_big_orders(orders: &Vec<Order>, min_lines: usize) -> Vec<String> {
    let mut result = Vec::new();
    for order in orders.clone() {
    // copies every order, with its lines
    if order.lines.len() >= min_lines {
    result.push(order.customer.clone());
    }
    }
    result
    }
    pub fn all_ids(orders: &[Order]) -> Vec<u64> {
    orders.iter().map(|o| o.clone()).map(|o| o.id).collect()
    }
    Run in Playground (opens the Rust Playground in a new tab)
  2. BetterIterates by reference, clones only the names that go into the result, and copies the u64 IDs.

    pub fn customers_with_big_orders(orders: &[Order], min_lines: usize) -> Vec<String> {
    orders
    .iter()
    .filter(|o| o.lines.len() >= min_lines)
    .map(|o| o.customer.clone()) // one clone per result: the result owns its names
    .collect()
    }
    pub fn all_ids(orders: &[Order]) -> Vec<u64> {
    orders.iter().map(|o| o.id).collect() // `u64` is `Copy`
    }
    Run in Playground (opens the Rust Playground in a new tab)

In the “worse” version, orders.clone() copies every order and every line before the loop starts, and o.clone() in all_ids copies each complete order only to read one number. Both functions give the correct result. The cost grows with the size of the data, and the code looks like it needs the copies, which misleads the next reader.

In the “better” version, the one remaining .clone() has a reason: the result Vec<String> owns its names. If the caller only needs to read the names while orders exists, the function could return Vec<&str> and clone nothing. That is a design choice that the reviewer can ask about. See also gallery entry 4.

  1. Which is cheaper: name.clone() on a String, or shared.clone() on an Arc<Vec<Order>>?

    Show the answer

    Usually the Arc clone. It adds one to a counter and copies a pointer. The String clone allocates and copies the text. The cost depends on the type, not on the method name.

  2. Why can a struct with a String field not derive Copy?

    Show the answer

    Copy is a bitwise copy. A bitwise copy of a String would give two owners of the same heap buffer, and both would free it. So String is not Copy, and neither is any type that contains one.

  3. You see for order in orders.clone(). What do you suggest?

    Show the answer

    for order in &orders or orders.iter(), to iterate by reference. If the loop body changes orders, the clone hides a borrow conflict. Then suggest a structure without the conflict, for example collecting the changes first and applying them after the loop.

  4. When is a Cow<'_, str> return type a good choice?

    Show the answer

    When the function usually returns its input unchanged, and only sometimes must make a modified copy. Then the common case allocates nothing.