Clone and Copy
In one sentence
Section titled “In one sentence”Clone is an explicit, visible copy that can be expensive; Copy is an implicit bitwise copy for small types that own no heap memory.
The Java you know
Section titled “The Java you know”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 namesvar copy = new Invoice(invoice.number(), invoice.total(), new ArrayList<>(invoice.lines())); // a copy, by handRust
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` tooIn 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().
The Rust you will see
Section titled “The Rust you will see”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,}- Line 1:
CopyrequiresClone, so both are in the list.Moneyhas only ani64, so it can beCopy.PartialOrdandOrdadd comparison. - Line 6:
Invoicecan beClonebut notCopy, because it has aStringfield. - Line 8: A
Stringowns 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.
How to read it
Section titled “How to read it”When you see .clone(), ask what type is cloned. The cost depends on the type, not on the method name.
Cheap clones: Rc and Arc
Section titled “Cheap clones: Rc and Arc”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).
When clone() is correct
Section titled “When clone() is correct”RcandArcclones: cheap, and the normal way to share.- Small data: a short
Stringthat 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.
When clone() is a smell
Section titled “When clone() is a smell”- 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: copy only when you must
Section titled “Cow: copy only when you must”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.
What good looks like
Section titled “What good looks like”- 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
&Tor&[T]and clone only the parts that they must own. Copyon small value types. IDs, money amounts, coordinates and small enums deriveCopy(andClone,Debug,PartialEq,Eq). Then they behave like Java primitives.- No
Copyon types that might grow. Adding aStringfield to aCopytype later is a breaking change, because callers rely on the implicit copy. Rc::clone(&x)orArc::clone(&x)where the codebase wants cheap clones to look different from deep ones.
Red flags in AI output
Section titled “Red flags in AI output”.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)whenfonly 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 withArc, or the functions should borrow.
- S01Severity: Medium
clone()to satisfy the borrow checkerA copy of the data is made only to make a borrow error go away.
Better vs worse
Section titled “Better vs worse”The task: from a list of orders, get the customers with many order lines, and all order IDs.
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 linesif 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) BetterIterates by reference, clones only the names that go into the result, and copies the
u64IDs.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.
Check yourself
Section titled “Check yourself”Which is cheaper:
name.clone()on aString, orshared.clone()on anArc<Vec<Order>>?Show the answer
Usually the
Arcclone. It adds one to a counter and copies a pointer. TheStringclone allocates and copies the text. The cost depends on the type, not on the method name.Why can a struct with a
Stringfield not deriveCopy?Show the answer
Copyis a bitwise copy. A bitwise copy of aStringwould give two owners of the same heap buffer, and both would free it. SoStringis notCopy, and neither is any type that contains one.You see
for order in orders.clone(). What do you suggest?Show the answer
for order in &ordersororders.iter(), to iterate by reference. If the loop body changesorders, 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.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.
Go deeper
Section titled “Go deeper”- Standard library: the
Clonetrait - Standard library: the
Copytrait, including “When should my type beCopy?” - Standard library:
Cow - The Rust Programming Language, chapter 4.1: Variables and data interacting with clone