| |

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:

TypeReason
i8–i128, u8–u128Fixed-size scalars
f32, f64Fixed-size scalars
boolOne byte
charFour bytes
&TA reference is a pointer
*const T, *mut TRaw pointers
(T1, T2) where both are CopyTuples of Copy types
[T; N] where T is CopyArrays of Copy types
Option<T> where T is Copy
Structs and enums with all-Copy fieldsWhen 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.

TraitMethodImplicitCost
CopyNoneYesBitwise copy
Cloneclone(&self) -> SelfNoMay 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.

TypeCopyCloneReason
i32, f64, bool, charYesYesScalars
&TYesYesReference
(i32, i32)YesYesTuple of scalars
[u8; 32]YesYesArray of scalars
Small struct of scalarsDerivableDerivableAll fields Copy
StringNoYesOwns heap
Vec<T>NoYesOwns heap
Box<T>NoYesOwns heap
Rc<T>, Arc<T>NoYesReference count
File, TcpStreamNoNoOwns OS resource
MutexGuardNoNoOwns 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

AspectCopyClone
ImplicitYesNo
MethodNoneclone()
CostBitwise copyMay allocate
RequiresAll fields CopyAll fields Clone
With DropCannotCan
DerivableYesYes

Types by Trait

TypeCopyClone
i32, f64, bool, charYesYes
&TYesYes
(i32, i32)YesYes
[u8; 4]YesYes
StringNoYes
Vec<T>NoYes
Box<T>NoYes
Rc<T>, Arc<T>NoYes
File, TcpStreamNoNo
MutexGuardNoNo

Derive Rules

DeriveRequirement
#[derive(Copy)]All fields Copy; also requires Clone
#[derive(Clone)]All fields Clone
BothAll fields Copy

Costs

OperationCost
Copy a Copy typeBitwise copy
Move a non-Copy typeCopy stack part
Clone a StringAllocate + copy
Clone an RcIncrement count
Clone a VecAllocate + 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

PitfallWhy It HappensFix
Clone of Copy typeNot knowing it is CopyRemove the clone
Copy type moves unexpectedlyType is not CopyDerive Copy or use a reference
Copy derive failsA field is not CopyRemove Copy or make the field Copy
Copy and Drop conflictBoth implementedRemove Copy
Clone does not deep-copyDerived clone copies shallowlyImplement manually
Hidden cost of cloneClone in a hot loopMove 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

ItemValue
CopyMarker trait for bitwise-copyable types
Copy requiresClone, all fields Copy
Copy behaviorImplicit on assignment and calls
CloneTrait with clone() method
Clone behaviorExplicit via .clone()
Clone costMay allocate
Copy and DropMutually exclusive
Derive#[derive(Copy, Clone)]
Rc::cloneIncrements count, does not copy value
Large typesShould not be Copy

Key takeaways:

  • Copy is 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.
  • Clone is explicit duplication. It is invoked with .clone(), and it may allocate and copy deeply. The explicit call makes the cost visible.
  • Copy requires Clone. Every copyable type is cloneable, and cloning a Copy type is the same as copying it. The Clone derive is required alongside Copy.
  • A type cannot be both Copy and Drop. 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-Copy fields can derive Copy. A struct with a String field cannot be Copy because the String owns a heap allocation.
  • Rc and Arc implement Clone manually. The clone increments the reference count rather than duplicating the value. This is why they are not Copy: a bitwise copy would not update the count.
  • Large types should not be Copy. Copy is implicit, so a large Copy type would silently copy on every use. Moving is cheaper.
  • Derive Copy for 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!