Copy Versus Clone in Rust and Which Types Are Copy
Copy is an implicit bitwise duplicate and Clone is an explicit call that can do real work. Know which of your types should have which, and what each costs.

Copy and Clone both mean "make another one of these", and Rust has both because the two meanings differ in a way that matters for performance and for correctness. Copy is implicit, free and limited to types whose value is just bits. Clone is explicit, may cost an allocation and works for anything that knows how to duplicate itself. The difference decides whether let b = a; moves the value or duplicates it, and whether using a afterward compiles.
This post shows both traits at work with a program that prints the results, lists which types are Copy, and shows the compiler error you get when you try to make a type Copy that cannot be. It ran on Rust 1.99 in edition 2024, and every output below is the program's own.
The difference in one program
use std::mem::size_of;
#[derive(Debug, Clone, Copy, PartialEq)]
struct Point {
x: f64,
y: f64,
}
#[derive(Debug, Clone)]
struct Label {
text: String,
at: Point,
}
fn main() {
// Copy: assignment duplicates the bits, and the original stays usable.
let p = Point { x: 1.0, y: 2.0 };
let q = p;
println!("p = {p:?}, q = {q:?}");
// Not Copy: assignment moves. Clone is the explicit, possibly expensive duplicate.
let a = Label { text: "origin".to_string(), at: p };
let b = a.clone();
println!("a = {a:?}");
println!("b = {b:?}");
println!("same heap buffer? {}", a.text.as_ptr() == b.text.as_ptr());
println!("size_of Point = {} bytes (bits copied)", size_of::<Point>());
println!("size_of String = {} bytes on the stack, plus the heap buffer", size_of::<String>());
println!("size_of Label = {} bytes", size_of::<Label>());
}p = Point { x: 1.0, y: 2.0 }, q = Point { x: 1.0, y: 2.0 }
a = Label { text: "origin", at: Point { x: 1.0, y: 2.0 } }
b = Label { text: "origin", at: Point { x: 1.0, y: 2.0 } }
same heap buffer? false
size_of Point = 16 bytes (bits copied)
size_of String = 24 bytes on the stack, plus the heap buffer
size_of Label = 40 bytesStart with Point. It derives Copy, so let q = p; duplicates its 16 bytes, two f64 values, and both p and q stay usable. Nothing was moved. There is no function call to inspect and no allocation: the compiler emits a copy of the bits, the same as for an integer. That is what Copy means: a type is Copy when duplicating its bytes yields a valid, independent value.
Label is different. It contains a String, which is a pointer to a heap buffer, a length and a capacity. If assignment copied those 24 bytes, two String values would point at the same buffer, and when both were dropped the buffer would be freed twice. So Label is not Copy, and let c = a; would move it. To get a second Label you ask for one explicitly with a.clone(), which runs Clone::clone: it allocates a new buffer for the text, copies the characters and builds an independent value. The line same heap buffer? false confirms it. The two labels hold equal text at different addresses, so changing one cannot affect the other.
The last three lines show the sizes. A String is 24 bytes on the stack whatever its content, and its characters live elsewhere. That second location is what Clone has to copy and Copy cannot.
Which types are Copy
A type can be Copy only if every part of it is Copy, and it must not own a resource that needs releasing.
| Copy | Not Copy |
|---|---|
Integers, floats, bool, char | String, Vec<T>, Box<T> |
Shared references &T | Mutable references &mut T |
| Raw pointers, function pointers | Rc<T>, Arc<T> (they implement Clone but not Copy) |
| Tuples and arrays of Copy types | A type that implements Drop |
Your structs and enums that derive Copy and contain only Copy types | Any type holding a non-Copy field |
Two entries in the table are worth noticing. Shared references are Copy, so passing &data to several functions at once is free and needs no clone. Mutable references are not, because two copies of a &mut would be two writers. And a type that implements Drop cannot be Copy, since copying it would run its destructor twice.
The compiler error when a type cannot be Copy
Adding Copy to a struct with a String field is a mistake beginners try, and the compiler's answer is exact:
#[derive(Clone, Copy)]
struct Label {
text: String,
id: u32,
}error[E0204]: the trait `Copy` cannot be implemented for this type
--> e0204.rs:2:8
|
1 | #[derive(Clone, Copy)]
| ---- in this derive macro expansion
2 | struct Label {
| ^^^^^
3 | text: String,
| ------------ this field does not implement `Copy`The message names the field that blocks it. The fix is to remove Copy and keep Clone, or to change the field to something Copy, such as &'static str or an ID that refers to the text stored elsewhere. Deriving Clone alone is always allowed when every field is Clone, which is nearly always.
How the two traits relate
Copy is declared as trait Copy: Clone, so every Copy type is also Clone, and for a Copy type, clone() is just a copy. You cannot implement Copy without Clone, which is why the derive lists both. In generic code, a bound of T: Copy lets you use a value after passing it by value, without writing .clone(), and a bound of T: Clone lets you duplicate it explicitly. Choose the weakest bound that works: Clone accepts more types.
The other difference is visibility. Copy happens silently on assignment, argument passing and returns. Clone is always a method call you can see in the source and search for. That is deliberate. A silent copy of a 16-byte Point is harmless, and a silent copy of a million-element Vec would be a performance bug. Rust makes the second kind visible by requiring .clone(), so reading code tells you where the expensive duplicates are.
When to derive Copy for your own types
Derive Copy for small, plain values: points, colors, IDs, handles, flags, simple enums without data. A good rough test is that the type is as cheap to pass by value as a few integers, and that having two independent copies makes sense. Do not derive it for a type that models a unique resource, such as a file handle, a lock guard or an owned buffer, even when every field happens to be Copy. The point of such a type is that there is one of it, and making it Copy removes the compiler's help in keeping it that way. Remember too that adding Copy is a promise to your users: removing it later is a breaking change, since code relying on implicit copies stops compiling.
A common design question is the size limit. There is no rule, but a type above roughly 32 to 64 bytes is better passed by reference even when it is Copy, because each implicit copy moves that many bytes. Mark such a type Clone only if copies are rare, and pass &T otherwise.
Clone costs, and when to avoid it
A clone's cost is proportional to the data it owns: String::clone copies the characters, Vec::clone copies every element and clones each one, and a Vec<String> clone does an allocation per string. Rc::clone and Arc::clone copy only a pointer and bump a counter, which is why they are idiomatic for sharing. In a hot loop, a .clone() that you added to appease the borrow checker is the first thing to look at when a profile shows time in malloc. The usual alternatives are to borrow (&T), to move the value when you no longer need the original, or to restructure so that only one place needs ownership. Ownership in Rust explained covers how to choose.
Cloning is also fine, and often right, in code that is not on a hot path. A configuration struct cloned once at startup, a string copied to give a thread its own value: these are cheap, clear and keep the code simple. The habit to build is to clone deliberately, knowing what it costs, and not to treat the borrow checker's errors as a signal to sprinkle .clone() until they stop. The latter works and teaches nothing; the former is a decision. For the related error that tells you when you are asking for a move you cannot have, see E0382, borrow of a moved value.


