Question

Rust Copy and Clone for Enums Containing String

Question

How can I implement Clone and Copy for a Rust enum when one variant contains a String, while other variants contain copyable values or arrays?

#[derive(Clone, Copy)]
enum Simple {
    Error(String),
    Okay,
    Foo([u32; 5]),
}

fn main() {
    let x = Simple::Error(String::from("blah"));
    let y = x.clone();
}

The compiler reports that String does not implement Copy. Why is String not copyable, and can Clone be implemented only for the Error(String) variant while the other variants use the default behavior?

Short Answer

You will learn the difference between Rust's Copy and Clone traits, why a single non-Copy field prevents an entire enum from being Copy, and how #[derive(Clone)] safely clones every variant. You will also see when to redesign a type, borrow text, or use shared ownership instead.

Concept

Copy and Clone both create another usable value, but they have importantly different rules.

  • Copy means Rust may duplicate a value automatically through a simple bitwise copy. Assigning or passing the value does not move it.
  • Clone means creating a duplicate requires explicit code: .clone(). The clone may allocate memory or perform other work.

A String owns heap-allocated text. Internally, it stores metadata that points to a heap buffer. If Rust bitwise-copied a String, both values would believe they owned the same buffer. When both values were dropped, Rust would attempt to free that memory twice, which is unsafe.

Therefore, String implements Clone, which allocates a separate buffer and copies the text, but it deliberately does not implement Copy.

For an enum to implement Copy, every field in every variant must implement Copy. It is not enough that most variants are copyable. Since Simple::Error contains a String, Simple cannot safely be Copy.

The usual solution is simply:

#[derive(Clone)]
enum Simple {
    Error(String),
    Okay,
    Foo([u32; 5]),
}

The derived Clone implementation handles each variant correctly. It clones the String for Error, and clones the array for Foo. Since u32 is Copy, cloning [u32; 5] is inexpensive and effectively copies its elements.

Mental Model

Think of Copy as photocopying a sheet of paper: the copy can be made instantly and independently because all information is directly on the page.

Think of String as a library-card key to a private storage locker. A raw photocopy of the key card would make two people think they both own the same locker contents. Both might try to empty and close the locker, causing a conflict.

Clone handles this properly: it creates a new locker and puts a duplicate of the text into it. This takes work, which is why you must explicitly call .clone().

An enum is like one labeled container that can hold different kinds of contents. Rust must apply the same Copy promise to the entire container, including every possible variant. One ownership-managing variant means the whole enum cannot be photocopied automatically.

Take Quiz

Syntax and Examples

Use #[derive(Clone)] when all fields support Clone:

#[derive(Clone, Debug)]
enum Simple {
    Error(String),
    Okay,
    Foo([u32; 5]),
}

fn main() {
    let x = Simple::Error(String::from("blah"));
    let y = x.clone();

    println!("x: {x:?}");
    println!("y: {y:?}");
}

x.clone() calls the compiler-generated Clone implementation for Simple.

  • For Simple::Error(message), it calls message.clone().
  • For Simple::Okay, there is no data to clone.
  • For Simple::Foo(numbers), it clones the array and its elements.

If an enum contains only Copy fields, it can derive both traits:

#[derive(Clone, Copy, Debug)]
enum Status {
    Okay,
    Code(u32),
    Values([u32; 5]),
}

fn main() {
    let first = Status::Code(200);
    let second = first; // Copies; first remains usable.

    println!("{first:?}, {second:?}");
}

Copy requires Clone as a supertrait, so a Copy type must also implement Clone. In practice, derive both together for fully copyable value types.

Step by Step Execution

Consider this example:

#[derive(Clone, Debug)]
enum Simple {
    Error(String),
    Okay,
    Foo([u32; 5]),
}

fn main() {
    let x = Simple::Error(String::from("disk full"));
    let y = x.clone();

    println!("{x:?}");
    println!("{y:?}");
}

Step by step:

  1. String::from("disk full") allocates owned storage for the text.
  2. Simple::Error(...) stores that owned String inside x.
  3. x.clone() checks which variant x contains. It is Error.
  4. Rust calls String::clone() for the contained message.
  5. String::clone() creates separately owned text with the same characters.
  6. y becomes its own Simple::Error value.
  7. Both x and y can be used and later dropped safely because each string owns a separate allocation.

By contrast, this assignment moves instead of clones:

let x = Simple::Error(String::from("disk full"));
let y = x;

// println!("{x:?}"); // Error: x was moved into y.
println!("{y:?}");

A move transfers ownership without duplicating the string. It is efficient because no new text allocation is needed.

Real World Use Cases

Clone is useful when a program genuinely needs two independently owned values.

  • Error reporting: Keep an error message in a result object while also sending a copy to a logging system.
  • Configuration: Clone a configuration value before moving one copy into a worker thread or task.
  • API data: Duplicate request data when separate processing steps need ownership of it.
  • Collections: Copy or clone a value before inserting it into multiple collections.
  • State snapshots: Clone application state before making a tentative change, such as in an editor's undo system.

Copy is useful for small, simple values where duplication is cheap and has no ownership consequences.

  • Numeric types such as u32 and f64
  • Booleans and characters
  • Small identifiers and flags
  • Fixed-size structs containing only Copy fields
  • Fixed-size arrays whose elements are Copy

Do not add Copy merely for convenience. Its presence changes how assignment and function calls behave, so it should express that implicit duplication is cheap and semantically appropriate.

Take Quiz

Real Codebase Usage

In real Rust codebases, developers usually derive Clone for data types that contain owned strings, vectors, maps, or other heap-owning values:

#[derive(Clone, Debug)]
pub enum ApiResult {
    Success { body: String },
    NotFound,
    InvalidRequest(String),
}

They then avoid cloning unless ownership actually needs to be duplicated. Often, a function can borrow instead:

fn log_result(result: &Simple) {
    println!("Result: {result:?}");
}

Borrowing with &Simple lets the function inspect the value without taking ownership and without allocating a clone.

A common validation pattern uses a borrowed string slice:

fn is_valid_name(name: &str) -> bool {
    !name.trim().is_empty()
}

The function accepts &str, so callers can pass either a String by reference or a string literal without cloning.

When several parts of a program must share the same owned text, developers may use Rc<str> for single-threaded code or Arc<str> for thread-safe shared ownership. These types are Clone, but cloning them usually increments a reference count rather than duplicating all text.

Manual Clone implementations are reserved for custom behavior, such as skipping a cache or regenerating derived state. For ordinary enums, #[derive(Clone)] is clearer and less error-prone.

Common Mistakes

Deriving Copy because most variants are copyable

This fails because Copy applies to the entire enum:

// Does not compile.
#[derive(Clone, Copy)]
enum Simple {
    Error(String),
    Okay,
}

Fix: remove Copy and keep Clone.

#[derive(Clone)]
enum Simple {
    Error(String),
    Okay,
}

Expecting assignment to clone a non-Copy value

let first = String::from("hello");
let second = first;

// println!("{first}"); // Error: first was moved.
println!("{second}");

Assignment moves ownership. Explicitly clone only if two owned strings are required:

let first = String::from("hello");
let second = first.clone();

Trying to implement Copy manually

A manual implementation cannot bypass Rust's safety rule:

// This cannot compile because Simple does not meet Copy's requirements.
impl Copy for Simple {}

Every field must already be Copy.

Writing a partial Clone implementation unnecessarily

You cannot implement Clone separately for only one enum variant. Traits are implemented for the whole type. A manual implementation must handle every variant:

impl Clone for Simple {
    fn clone(&self) -> Self {
        match self {
            Self::Error(message) => Self::Error(message.clone()),
            Self::Okay => Self::Okay,
            Self::Foo(values) => Self::Foo(*values),
        }
    }
}

This works, but #[derive(Clone)] produces the appropriate behavior automatically in this case.

Cloning when borrowing is enough

Avoid this when print_message only reads the value:

fn print_message(message: String) {
    println!("{message}");
}

let message = String::from("hello");
print_message(message.clone());

Prefer borrowing:

fn print_message(message: &str) {
    println!("{message}");
}

let message = String::from("hello");
print_message(&message);

Comparisons

FeatureCopyCloneMove
Happens automatically on assignment?YesNoYes, for non-Copy values
Requires .clone()?NoYesNo
Can allocate memory?NoYes, depending on the typeNo
Works with String?NoYesYes, ownership is transferred
Original remains usable?YesYesNo
Typical typesu32, bool, small plain structsString, Vec<T>, enums with owned dataAny non-Copy owned value

&str and String are also different:

TypeOwns text?Usually Copy?Use when
StringYesNoYou need dynamically created or stored owned text
&strNo, it borrows textYesYou only need to read text temporarily

A String owns data, while an &str is a lightweight borrowed view. The lifetime rules for &str ensure that the referenced text remains valid.

Take Quiz

Cheat Sheet

// An enum with owned data: derive Clone, not Copy.
#[derive(Clone)]
enum Message {
    Error(String),
    Okay,
}

let a = Message::Error(String::from("failed"));
let b = a.clone(); // Two independent values.
// Assignment moves a non-Copy value.
let b = a;
// a cannot be used here.
// Borrow instead of cloning when only reading.
fn show(message: &str) {
    println!("{message}");
}

Rules:

  • A type can implement Copy only when all of its fields are Copy.
  • Every Copy type must also implement Clone.
  • String, Vec<T>, Box<T>, and most owning containers are not Copy.
  • #[derive(Clone)] calls clone() for each field and variant.
  • You cannot implement a trait for only one enum variant.
  • Prefer borrowing (&T or &str) when duplication is unnecessary.
  • Use .clone() deliberately because it may allocate or copy significant data.

FAQ

Why does String not implement Copy in Rust?

String owns heap memory. A bitwise copy would create two values that try to free the same allocation, so Rust requires an explicit clone() that creates separate owned storage.

Can an enum be Copy if only one variant contains a String?

No. Copy must be valid for every possible enum variant. One field that is not Copy prevents the entire enum from implementing Copy.

Can I implement Clone for only the Error variant?

No. Trait implementations belong to the complete enum type, not individual variants. However, #[derive(Clone)] automatically performs the correct action for every variant.

Does #[derive(Clone)] copy a String correctly?

Yes. It calls String::clone(), which creates a new owned allocation containing the same text.

Is cloning an array of u32 expensive?

For a fixed small array such as [u32; 5], cloning copies its elements and is generally inexpensive. The array is also Copy because u32 is Copy.

Should I use clone() to fix ownership errors?

Not automatically. First consider whether a function can accept a borrow such as &Simple or &str. Clone when independent ownership is genuinely needed.

Can I use &str instead of String to make my enum Copy?

Sometimes, but only if the referenced text is guaranteed to live long enough. This introduces lifetimes and does not work for arbitrary dynamically created text without storing the owner elsewhere.

Take Quiz

Mini Project

Description

Build a small result classifier that stores either a successful numeric response, an empty result, or an owned error message. The project demonstrates that one enum can contain both copyable data and a String, while safely using Clone for the whole enum.

Goal

Create and clone result values without making the enum implement Copy, then display each result with pattern matching.

Requirements

  1. Define an enum with a String error variant, a unit variant, and a fixed numeric array variant.
  2. Derive Clone and Debug for the enum.
  3. Create an error value and clone it.
  4. Write a function that borrows the enum and prints a useful message for every variant.
  5. Demonstrate that both the original and cloned error value can be used.
Take Quiz

Keep learning

Related questions

View more