Copy lets an assignment or by-value argument duplicate a value while
leaving the original usable. A type that implements Drop cannot implement
Copy, even if its drop method does nothing.
For your own struct or enum, every field must also implement Copy.
Meeting these conditions makes the type eligible; it does not automatically
give it Copy. You must derive or implement the trait, along with its
supertrait Clone.
Eligibility is not an implementation
This complete program fails with E0382:
struct Point { x: i32 }
fn main() {
let a = Point { x: 3 };
let b = a;
println!("{} {}", a.x, b.x); // a was moved into b
}
Add #[derive(Clone, Copy)] above struct Point and the program compiles.
The field was already eligible for copying. The derive changes what callers
may do with the containing type.
A Drop implementation rules out Copy
This program fails with E0184:
#[derive(Clone, Copy)]
struct Session { id: i32 }
impl Drop for Session {
fn drop(&mut self) { println!("closing {}", self.id); }
}
fn main() {
let _session = Session { id: 7 };
}
Session has a Copy field, but its own Drop implementation disqualifies it.
Rust applies this rule to every Drop implementation; it does not inspect the
method to decide whether duplicating its effects would be harmless.
For a resource owner, implicit duplication could create two values claiming
responsibility for the same resource. Both might then try to release it.
Explicit Clone lets a type define how duplication works, such as allocating
separate storage or incrementing a shared ownership count.
A non-Copy field also rules it out
This program fails with E0204:
#[derive(Clone, Copy)]
struct Record { id: i32, label: String }
fn main() {
let record = Record { id: 1, label: String::from("draft") };
println!("{}: {}", record.id, record.label);
}
String is not Copy, so Record cannot be Copy. Copying the bytes that
describe a String would not create another independently owned text buffer.
Cloning a String does duplicate its contents.
A struct does not need its own Drop implementation to require cleanup:
its fields are dropped automatically. Conversely, the absence of cleanup
does not prove Copy. For example, &mut T is not Copy because duplicating
an exclusive reference would violate its access rules.
References copy access, not the referenced value
| Type | Copy? | Reason |
|---|---|---|
i64, f64, bool, char |
Yes | These primitive types implement Copy. |
(i32, bool), [u8; 16] |
Yes | Their elements are Copy. |
&T |
Yes | Duplicating shared access is permitted, even if T is not Copy. |
&mut T |
No | Exclusive access cannot be implicitly duplicated. |
Option<T> |
If T is Copy | Its implementation requires a Copy payload. |
String, Vec<T>, Box<T> |
No | They own storage or values requiring ownership management. |
Rc<T>, Arc<T> |
No | Cloning updates an ownership count; dropping releases a share. |
MutexGuard<'_, T> |
No | Dropping the guard releases its lock. |
A shared reference to a String is Copy even though String is not. Copying the reference leaves both references pointing to the same String. It does not copy the String or make the underlying data independent. The referent's Drop implementation does not become a Drop implementation on the reference.
Mutable references can often be reused because calls implicitly reborrow them. This complete program calls a function twice through the same reference:
fn increment(n: &mut i32) { *n += 1; }
fn main() {
let mut n = 0;
let r = &mut n;
increment(r);
increment(r);
assert_eq!(*r, 2);
}
Each call temporarily borrows through r. This does not make &mut i32
Copy. Other uses, such as moving r into a new binding, can move the reference.
Copy and Clone have different roles
Copy is implicit and its behavior cannot be customized. Clone is an explicit
method call whose implementation may perform additional work. Because
Copy: Clone, a function accepting T: Clone can accept Copy types too.
For a Copy type, Clone should return the same value as *self. The compiler
does not enforce that equivalence for a handwritten Clone implementation.
Clippy's non_canonical_clone_impl warns about noncanonical implementations;
a warning becomes an error under -D warnings.
Deriving both traits is usually sufficient. Manual implementations can avoid unnecessary bounds introduced by derive. This wrapper only copies a shared reference, so T itself does not need to be Copy or Clone:
struct View<'a, T: ?Sized>(&'a T);
impl<T: ?Sized> Copy for View<'_, T> {}
impl<T: ?Sized> Clone for View<'_, T> {
fn clone(&self) -> Self { *self }
}
fn main() {
let text = String::from("draft");
let a = View(&text);
let b = a;
assert!(std::ptr::eq(a.0, b.0));
assert_eq!(a.0, "draft");
}
Deriving Copy and Clone for this generic wrapper would add bounds on T.
In contrast, a Pair<T> { a: T, b: T } really does need T to be Copy before
the pair can be Copy: it stores the values themselves.
Copy is not a promise of zero work. Copying a large array can move many bytes; the optimizer may eliminate some copies, but that depends on the program. Both moves and copies can involve copying bytes in memory. Their ownership rules differ regardless of the generated instructions.
Treat Copy as part of your public API
Removing Copy can break callers that use a value after passing it by value. If a type may later need a String field or custom cleanup, consider leaving it non-Copy from the start. A type can be eligible for Copy without exposing that behavior as a promise to callers.