Rust 14 🦀 Deep Copying with Clone vs Bitwise Copy with Copy
Rust gives every type one of two behaviors on assignment: it either moves or it copies. A move transfers ownership and invalidates the source. A copy duplicates the value and leaves both the source and the destination valid. The Copy and Clone traits control which behavior applies. Copy is the marker for types that can be duplicated with a simple bitwise copy — no heap allocation, no resource release. Clone is the trait for explicit duplication, which may allocate and may be expensive.
The distinction matters because it determines what happens when a value is assigned, passed to a function, or returned. A type that implements Copy is duplicated silently; there is no way to prevent the copy, and no way to invalidate the source. A type that implements only Clone must be cloned explicitly with .clone(); otherwise, it moves. The compiler enforces this, and the choice of which trait to implement — or derive — has consequences for both correctness and performance.
This chapter covers what Copy means, what Clone means, the difference between them, which types implement which, how to derive both, why a type cannot implement both Copy and Drop, and the patterns that use each correctly.
Key point: Copy is a marker trait for types that can be duplicated with a bitwise copy. It requires Clone and applies implicitly to assignments and function calls. Clone is the trait for explicit duplication, invoked with .clone(), and may allocate. A type cannot implement both Copy and Drop, because dropping a copy would free the same resource twice. Derive Copy for small, resource-free types; implement Clone for types that need a deep copy.
Why Copy and Clone exist
The performance problem. Some types are trivially copyable. An i32 is four bytes; copying it is a single machine instruction. A String owns a heap allocation; copying it requires allocating new memory and copying the contents. Treating both the same would be either unsafe (if String were copied bitwise, both copies would free the same heap) or slow (if i32 required an explicit .clone() call).
The safety problem. A bitwise copy of a type that owns a resource creates two values that both believe they own the resource. When both go out of scope, both free it, which is a double-free. Copy is restricted to types with no resources to free, so bitwise copying is always safe.
The ergonomics problem. Copy types can be used after assignment without a .clone() call, which makes code that deals with numbers, booleans, and small structs more natural. Without Copy, every use of a number would require a clone or a reference.
The explicitness problem. Clone requires the caller to write .clone(). This makes the cost visible: the reader sees that a deep copy is happening and can evaluate whether it is necessary. A hidden deep copy would be a performance trap.
The correctness problem. A type that owns a resource must not be Copy. A type that does not own a resource can be Copy. The compiler enforces this by forbidding Copy on types that implement Drop, and by requiring all fields to be Copy for a struct or enum to derive Copy.
a. What Copy means
Copy is a marker trait. It has no methods. A type that implements Copy is duplicated on assignment rather than moved.
let x = 5;
let y = x;
println!("{} {}", x, y); // 5 5 — both valid
The i32 implements Copy, so let y = x copies the value. x remains usable. This behavior applies to function calls and returns as well:
fn double(n: i32) -> i32 {
n * 2
}
let x = 5;
let y = double(x);
println!("{} {}", x, y); // 5 10 — x is still valid
The function receives a copy of x, and the original remains valid.
Types that implement Copy:
| Type | Reason |
|---|---|
i8–i128, u8–u128 | Fixed-size scalars |
f32, f64 | Fixed-size scalars |
bool | One byte |
char | Four bytes |
&T | A reference is a pointer |
*const T, *mut T | Raw pointers |
(T1, T2) where both are Copy | Tuples of Copy types |
[T; N] where T is Copy | Arrays of Copy types |
Option<T> where T is Copy | |
Structs and enums with all-Copy fields | When Copy is derived |
A struct or enum is Copy only if all its fields are Copy. The compiler can derive the implementation with #[derive(Copy, Clone)].
#[derive(Copy, Clone)]
struct Point {
x: i32,
y: i32,
}
The Clone derive is required alongside Copy because Copy requires Clone as a supertrait. Every Copy type is cloneable, but cloning a Copy type is the same as copying it.
A type cannot implement Copy if it implements Drop. The reason is that dropping a copy would free a resource that the original still owns:
#[derive(Copy, Clone)]
struct Resource {
handle: u32,
}
impl Drop for Resource {
fn drop(&mut self) {
// free the resource
}
}
// Error: the trait `Copy` cannot be implemented for this type
// because it has a destructor
The compiler rejects the combination because it would be unsound. A resource-owning type must be moved, not copied.
b. What Clone means
Clone is a trait with a clone method. It is used for explicit duplication and may allocate or copy deeply.
let s1 = String::from("hello");
let s2 = s1.clone();
println!("{} {}", s1, s2); // both valid
The String‘s clone allocates a new heap block and copies the characters. Both s1 and s2 own independent allocations, so both are valid.
The Clone trait can be derived for structs and enums:
#[derive(Clone)]
struct Person {
name: String,
age: u32,
}
The derived implementation clones each field. If a field is Copy, the clone is a copy. If a field is Clone but not Copy, the clone calls the field’s clone method. If a field is neither, the derive fails and a manual implementation is required.
A type can implement Clone without Copy. A String does: it is cloneable but not copyable, because copying it bitwise would alias the heap.
A type can implement both Copy and Clone. A Point with two i32 fields does: it is copyable because all fields are Copy, and the Clone implementation is trivially the same as the copy.
| Trait | Method | Implicit | Cost |
|---|---|---|---|
Copy | None | Yes | Bitwise copy |
Clone | clone(&self) -> Self | No | May allocate |
c. Copy versus Clone in practice
The difference shows up on every assignment, function call, and return. For a Copy type, the value is duplicated silently and both bindings remain valid. For a non-Copy type, the value moves unless .clone() is called.
#[derive(Copy, Clone)]
struct Point { x: i32, y: i32 }
#[derive(Clone)]
struct Person { name: String }
let p1 = Point { x: 1, y: 2 };
let p2 = p1;
println!("{:?} {:?}", p1, p2); // both valid
let person1 = Person { name: String::from("Alice") };
let person2 = person1.clone();
println!("{} {}", person1.name, person2.name); // both valid
// Without .clone(), person1 would be moved.
The Point is copied implicitly because it is Copy. The Person is cloned explicitly because it is not. The .clone() call is the signal that a deep copy is happening.
Cloning a Copy type is allowed but pointless:
let x = 5;
let y = x.clone(); // works, but equivalent to let y = x;
The compiler may warn about this, and idiomatic code omits the clone for Copy types.
d. Deriving Copy and Clone
The derive macro generates the implementations. For Copy, it requires all fields to be Copy. For Clone, it requires all fields to be Clone.
#[derive(Copy, Clone)]
struct Point {
x: i32,
y: i32,
}
For a type with mixed fields, Copy cannot be derived:
#[derive(Clone)]
struct Mixed {
id: u32, // Copy
name: String, // not Copy
}
The Mixed type can be cloned but not copied. The derived Clone implementation copies the id and clones the name.
A manual Clone implementation is sometimes needed when the derived one is not correct — for example, when the type contains a reference-counted pointer and the clone should increment the count rather than copy the value. Rc<T> implements Clone manually for this reason.
use std::rc::Rc;
let a = Rc::new(5);
let b = a.clone(); // increments the reference count
println!("{}", Rc::strong_count(&a)); // 2
The Rc::clone does not duplicate the 5; it increments the reference count and returns a new Rc pointing to the same allocation. This is why Rc is not Copy: a bitwise copy would not update the count, and the allocation would be freed twice.
e. Why Copy and Drop are mutually exclusive
A type cannot implement both Copy and Drop. The reason is that Drop is meant to release a resource, and a Copy type is duplicated implicitly. If both were allowed, a copy would produce two values that both believe they own the resource, and both would attempt to release it when they go out of scope.
// This is rejected by the compiler
#[derive(Copy, Clone)]
struct FileHandle {
fd: i32,
}
impl Drop for FileHandle {
fn drop(&mut self) {
// close(self.fd);
}
}
The compiler reports:
error[E0184]: the trait `Copy` cannot be implemented for this type;
the type has a destructor
The restriction is sound and necessary. A type with a destructor owns a resource, and the ownership must be unique. Moves transfer ownership; copies would duplicate it. Therefore, a resource-owning type must move, not copy.
The consequence is that file handles, sockets, mutex guards, and any type with a Drop implementation are always moved. They can be cloned if the clone acquires an independent resource, but the clone is explicit, not implicit.
f. Choosing between Copy and Clone
The decision of whether to derive Copy is based on what the type represents.
Derive Copy when the type is small and resource-free. A Point, a Color, a Range, a configuration flag — these are values that can be duplicated freely, and there is no cost to doing so.
Do not derive Copy when the type owns a resource. A String, a Vec, a File, a MutexGuard — these own something that must be released exactly once. They can implement Clone if a deep copy makes sense, but they must move.
Do not derive Copy when the type is large. A struct with a large inline array is technically copyable, but copying it on every assignment is expensive. Rust’s Copy semantics are implicit, so a large Copy type would silently copy megabytes on every use. In that case, moving is cheaper, and the type should not implement Copy.
Implement Clone manually when the derived behavior is wrong. The derived Clone does a field-by-field clone, which is correct for most types. But for types like Rc, Arc, or smart pointers in general, the manual implementation increments a reference count rather than duplicating the underlying value.
| Type | Copy | Clone | Reason |
|---|---|---|---|
i32, f64, bool, char | Yes | Yes | Scalars |
&T | Yes | Yes | Reference |
(i32, i32) | Yes | Yes | Tuple of scalars |
[u8; 32] | Yes | Yes | Array of scalars |
| Small struct of scalars | Derivable | Derivable | All fields Copy |
String | No | Yes | Owns heap |
Vec<T> | No | Yes | Owns heap |
Box<T> | No | Yes | Owns heap |
Rc<T>, Arc<T> | No | Yes | Reference count |
File, TcpStream | No | No | Owns OS resource |
MutexGuard | No | No | Owns lock |
Complete Example Session
// ============================================
// PART 1: COPY TYPE
// ============================================
fn main() {
let x = 5;
let y = x;
println!("{} {}", x, y); // 5 5 — both valid
}
// ============================================
// PART 2: MOVE TYPE
// ============================================
fn main() {
let s1 = String::from("hello");
let s2 = s1;
println!("{}", s2);
// println!("{}", s1); // Error: use of moved value
}
// ============================================
// PART 3: CLONE TYPE
// ============================================
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone();
println!("{} {}", s1, s2); // both valid
}
// ============================================
// PART 4: DERIVE COPY AND CLONE
// ============================================
#[derive(Copy, Clone, Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p1 = Point { x: 1, y: 2 };
let p2 = p1;
println!("{:?} {:?}", p1, p2); // both valid
}
// ============================================
// PART 5: CLONE ONLY
// ============================================
#[derive(Clone, Debug)]
struct Person {
name: String,
}
fn main() {
let person1 = Person { name: String::from("Alice") };
let person2 = person1.clone();
println!("{:?} {:?}", person1, person2);
}
// ============================================
// PART 6: COPY IN FUNCTION CALL
// ============================================
fn double(n: i32) -> i32 {
n * 2
}
fn main() {
let x = 5;
let y = double(x);
println!("{} {}", x, y); // 5 10
}
// ============================================
// PART 7: MOVE IN FUNCTION CALL
// ============================================
fn consume(s: String) {
println!("{}", s);
}
fn main() {
let s = String::from("hello");
consume(s);
// s is no longer valid
}
// ============================================
// PART 8: COPY AND DROP CONFLICT
// ============================================
// The following does not compile:
//
// #[derive(Copy, Clone)]
// struct FileHandle { fd: i32 }
//
// impl Drop for FileHandle {
// fn drop(&mut self) { }
// }
//
// error[E0184]: the trait `Copy` cannot be implemented
// for this type; the type has a destructor
// ============================================
// PART 9: RC CLONE INCREMENTS COUNT
// ============================================
use std::rc::Rc;
fn main() {
let a = Rc::new(5);
let b = a.clone(); // increments count, does not copy 5
println!("count: {}", Rc::strong_count(&a)); // 2
}
// ============================================
// PART 10: EXPLICIT CLONE FOR CLARITY
// ============================================
fn main() {
let names = vec![
String::from("Alice"),
String::from("Bob"),
];
let cloned = names.clone(); // explicit deep copy
println!("{:?}", cloned);
println!("{:?}", names); // original still valid
}
These ten parts cover Copy types, move types, clone types, deriving Copy and Clone, clone-only types, copy in function calls, move in function calls, the Copy/Drop conflict, Rc::clone incrementing the count, and explicit cloning of a vector.
Quick Reference
Copy vs Clone
| Aspect | Copy | Clone |
|---|---|---|
| Implicit | Yes | No |
| Method | None | clone() |
| Cost | Bitwise copy | May allocate |
| Requires | All fields Copy | All fields Clone |
With Drop | Cannot | Can |
| Derivable | Yes | Yes |
Types by Trait
| Type | Copy | Clone |
|---|---|---|
i32, f64, bool, char | Yes | Yes |
&T | Yes | Yes |
(i32, i32) | Yes | Yes |
[u8; 4] | Yes | Yes |
String | No | Yes |
Vec<T> | No | Yes |
Box<T> | No | Yes |
Rc<T>, Arc<T> | No | Yes |
File, TcpStream | No | No |
MutexGuard | No | No |
Derive Rules
| Derive | Requirement |
|---|---|
#[derive(Copy)] | All fields Copy; also requires Clone |
#[derive(Clone)] | All fields Clone |
| Both | All fields Copy |
Costs
| Operation | Cost |
|---|---|
Copy a Copy type | Bitwise copy |
Move a non-Copy type | Copy stack part |
Clone a String | Allocate + copy |
Clone an Rc | Increment count |
Clone a Vec | Allocate + copy each element |
Best Practices
✅ Do This:
// Derive Copy for small resource-free types
#[derive(Copy, Clone)]
struct Point { x: i32, y: i32 }
// Clone explicitly when a deep copy is needed
let s2 = s1.clone();
// Use Rc::clone for shared ownership
let b = Rc::clone(&a);
// Let Copy types copy implicitly
let y = x;
// Implement Clone manually for smart pointers
impl<T> Clone for MyRc<T> {
fn clone(&self) -> Self {
self.increment_count();
MyRc { ptr: self.ptr }
}
}
❌ Don’t Do This:
// Clone a Copy type
let y = x.clone(); // ❌ unnecessary if x is Copy
// Derive Copy for a resource-owning type
#[derive(Copy, Clone)]
struct File { fd: i32 } // ❌ if it owns the fd
// Implement Copy and Drop
impl Drop for R { } // ❌ with #[derive(Copy)]
// Clone large types unnecessarily
let big_copy = big_vec.clone(); // ❌ if a move would do
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
Clone of Copy type | Not knowing it is Copy | Remove the clone |
| Copy type moves unexpectedly | Type is not Copy | Derive Copy or use a reference |
Copy derive fails | A field is not Copy | Remove Copy or make the field Copy |
Copy and Drop conflict | Both implemented | Remove Copy |
| Clone does not deep-copy | Derived clone copies shallowly | Implement manually |
| Hidden cost of clone | Clone in a hot loop | Move or borrow instead |
Real-World Examples
1. Copy Struct
#[derive(Copy, Clone)]
struct Point { x: i32, y: i32 }
2. Clone-Only Struct
#[derive(Clone)]
struct User { name: String, age: u32 }
3. Clone a Vector
let copy = original.clone();
4. Rc Clone
let shared = Rc::clone(&original);
5. Copy in Loop
for i in 0..10 {
process(i); // i is Copy
}
6. Move in Loop
for s in strings {
process(s); // each String is moved
}
7. Clone in Loop
for s in &strings {
process(s.clone()); // explicit clone
}
8. Manual Clone
impl<T> Clone for Stack<T> {
fn clone(&self) -> Self {
Stack { items: self.items.clone() }
}
}
9. Copy for Enum
#[derive(Copy, Clone)]
enum Direction { North, South, East, West }
10. No Copy for File
struct File { handle: u32 } // no Copy, no Clone
Visual
Copy vs Move
┌──────────────────────────────────────────────────────────────┐
│ COPY (i32): │
│ let x = 5; │
│ let y = x; │
│ ┌────┬────┐ │
│ │ x │ y │ both valid │
│ │ 5 │ 5 │ │
│ └────┴────┘ │
│ │
│ MOVE (String): │
│ let s1 = String::from("hi"); │
│ let s2 = s1; │
│ ┌────┬────┐ │
│ │ s1 │ s2 │ s1 invalid, s2 owns │
│ │ ╳ │ ──▶│ │
│ └────┴────┘ │
│ │
│ CLONE (String): │
│ let s2 = s1.clone(); │
│ ┌────┬────┐ │
│ │ s1 │ s2 │ both valid, separate allocations │
│ │ ──▶│ ──▶│ │
│ └────┴────┘ │
└──────────────────────────────────────────────────────────────┘
Which Types Are Copy
┌──────────────────────────────────────────────────────────────┐
│ COPY: │
│ ├── Scalars: i32, f64, bool, char │
│ ├── References: &T │
│ ├── Tuples of Copy types │
│ ├── Arrays of Copy types │
│ └── Structs with all-Copy fields (derived) │
│ │
│ NOT COPY: │
│ ├── String, Vec, Box (own heap) │
│ ├── Rc, Arc (reference count) │
│ ├── File, TcpStream (own OS resource) │
│ └── Any type implementing Drop │
└──────────────────────────────────────────────────────────────┘
Copy and Drop Conflict
┌──────────────────────────────────────────────────────────────┐
│ #[derive(Copy, Clone)] │
│ struct FileHandle { fd: i32 } │
│ │
│ impl Drop for FileHandle { │
│ fn drop(&mut self) { close(self.fd); } │
│ } │
│ │
│ Error: the trait `Copy` cannot be implemented │
│ for this type; the type has a destructor │
│ │
│ Reason: a copy would produce two owners of the same fd, │
│ and both would close it, causing a double-close. │
└──────────────────────────────────────────────────────────────┘
Rc Clone
┌──────────────────────────────────────────────────────────────┐
│ let a = Rc::new(5); │
│ let b = a.clone(); │
│ │
│ ┌─────────────────────────────────────┐ │
│ │ HEAP │ │
│ │ ┌───────────────┐ │ │
│ │ │ count: 2 │ │ │
│ │ │ value: 5 │ │ │
│ │ └───────────────┘ │ │
│ └──────────▲──────────────────────────┘ │
│ │ │
│ ┌──────┴──────┐ │
│ │ │ │
│ ┌───┴───┐ ┌───┴───┐ │
│ │ a │ │ b │ │
│ └───────┘ └───────┘ │
│ │
│ clone() increments the count; the value is not copied. │
└──────────────────────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
Copy | Marker trait for bitwise-copyable types |
Copy requires | Clone, all fields Copy |
Copy behavior | Implicit on assignment and calls |
Clone | Trait with clone() method |
Clone behavior | Explicit via .clone() |
Clone cost | May allocate |
Copy and Drop | Mutually exclusive |
| Derive | #[derive(Copy, Clone)] |
Rc::clone | Increments count, does not copy value |
| Large types | Should not be Copy |
Key takeaways:
Copyis a marker trait for types that can be duplicated with a bitwise copy. It applies implicitly on assignment, function call, and return, so the source remains valid.Cloneis explicit duplication. It is invoked with.clone(), and it may allocate and copy deeply. The explicit call makes the cost visible.CopyrequiresClone. Every copyable type is cloneable, and cloning aCopytype is the same as copying it. TheClonederive is required alongsideCopy.- A type cannot be both
CopyandDrop. Dropping a copy would free the same resource twice. The compiler rejects the combination, which is why resource-owning types are always moved. - Only types with all-
Copyfields can deriveCopy. A struct with aStringfield cannot beCopybecause theStringowns a heap allocation. RcandArcimplementClonemanually. The clone increments the reference count rather than duplicating the value. This is why they are notCopy: a bitwise copy would not update the count.- Large types should not be
Copy.Copyis implicit, so a largeCopytype would silently copy on every use. Moving is cheaper. - Derive
Copyfor small, resource-free types. Points, colors, flags, and small structs of scalars are good candidates. Types that own heap or OS resources are not.
Remember: Copy and Clone are the two ways a value can be duplicated in Rust. Copy is implicit, cheap, and restricted to types with no resources to free. Clone is explicit, may be expensive, and works for any type that can define what duplication means. The distinction exists for safety: a bitwise copy of a resource-owning type would create two owners, and both would free the resource. The compiler prevents this by forbidding Copy on types with Drop. When you derive Copy, you are telling the compiler that the type is safe to duplicate bitwise. When you call .clone(), you are explicitly asking for a deep copy and accepting the cost. Choosing the right trait for the right type is part of writing correct and efficient Rust.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!