Skip to content
Select theme

When should you take a reference instead of cloning?

Short answer: Look at the signature of the function you are calling. If it takes &str (or &T), pass a reference — nothing is copied. If it takes String (or T), it wants to own the value, so you either hand it over or clone it first. The clone is not forced on you by the borrow checker; it is forced by the signature. Which is why the usual fix is to change the signature, not to clone.

Start from a fresh project — cargo new ref-not-clone, then replace src/main.rs with the whole of each file below.

ADA!
hello Ada

This compiles and does the right thing. And it looks like the borrow checker made you clone. It did not. shout takes String by value: the argument is moved in, shout owns it for the duration of the call, and when shout returns the string is gone. The only way to still have a name afterwards is to pass a copy — so the caller clones.

name.clone() allocates a brand-new String on the heap and copies every byte into it, just so the original survives a function that only ever reads it. For "Ada" that is invisible. For a ten-megabyte log line inside a loop, that clone is the program’s cost.

Drop the .clone() and the compiler tells you exactly what the signature demanded:

error[E0382]: borrow of moved value: `name`
--> src/main.rs:8:22
|
6 | let name = String::from("Ada");
| ---- move occurs because `name` has type `String`, which does not implement the `Copy` trait
7 | shout(name);
| ---- value moved here
8 | println!("hello {name}");
| ^^^^ value borrowed here after move
|
note: consider changing this parameter type in function `shout` to borrow instead if owning the value isn't necessary
--> src/main.rs:1:16
|
1 | fn shout(name: String) {
| ----- ^^^^^^ this parameter takes ownership of the value
| |
| in this function
help: consider cloning the value if the performance cost is acceptable
|
7 | shout(name.clone());
| ++++++++
For more information about this error, try `rustc --explain E0382`.

shout(name) moves the string into the call. The variable name is empty afterwards — the value it owned is gone — so the later println! is a use of a moved value, E0382. Nothing here is about borrowing at all: the signature said “I take ownership”, and the caller cannot keep what it gave away.

A function that only reads the string should not ask for String. Let the signature say what the body actually does:

ADA!
hello Ada

Same output, no clone, no error. &name creates a reference to the string’s data, and the borrow ends when shout returns — the caller keeps the String, untouched. Two details make this the natural choice:

  • &str is a borrowed view of string data, so it accepts a String’s contents without owning them.
  • Passing &name — an &String — works anyway, because &String automatically converts to &str at the call site (deref coercion). Callers never have to convert by hand.

A reference is a pointer and a length — two machine words, no matter how long the string is. A clone is a heap allocation plus a full copy of the bytes. That is the whole trade, and the signature is where you choose.

Some functions do need to own the value: they store it somewhere that outlives the call — into a struct field, a Vec, a thread. Ownership is then the honest signature, and one copy has to happen somewhere. Put that copy in exactly one place: inside the function that keeps the data.

Ada vs Ada

The constructor takes &str and does the single .to_string() copy that has to exist, because the struct owns its name after the call. If new took String instead, every caller holding only a &str — or a String it still needs — would have to clone at the call site, and the same one copy would get made in a different place each time.

The rule of thumb falls out of this: functions borrow, owners own. A function that only looks at data takes a reference. A function that keeps data takes ownership — or takes a reference and does the one unavoidable copy inside, right next to the field it lands in. What you should never do is clone at a call site to stop a move error: that error is the signature telling you it wants ownership, and the answer is either give it or change the signature.