Rust | What Is The Difference Between Copy and Clone Trait?

As you learn more about Rust programming language, you find out functionalities that seem to work the same, when in reality they differ in subtle ways. This is the case for the Copy and Clone traits. This article will explain each trait and show you what makes each different from the otehr.

In Rust, the Copy and Clone traits main function is to generate duplicate values. The difference is that Copy implicitly generates duplicates off of the bits of an existing value, and Clone explicitly generates deep copies of an existing value, often resulting in a more expensive and less performant operation that duplicating values via the Copy trait.

This post will explain how the Copy and Clone traits work, how you can implement them when using custom types, and display a comparison table between these two traits to give you a better understanding of the differences and similarities between the two.

How The Copy Trait Works

As previously mentioned, the Copy trait generates an implicit duplicate of a value by copying its bits.

What does this mean?

Copying 0s and 1s

Information is stored in bits and bytes. A byte is a collection of 8 bits and a bit is either a 0 or a 1. Every time you have a value, whether it is a boolean, a number, a string, etc, the value is stored in unique byte configuration representing that value.

In other words, if you have the values, such as,

  • true
  • false
  • 1
  • -5
  • "hello"

to name a few, each value has a collection of bits that denotes their value.

Hence, when you generate a duplicate using the Copy trait, what happens behind the scenes is copying the collection of 0s and 1s of the given value.

Implicit Duplicate

One of the key words you see in the definition of the Copy trait is the word “implicit”.

The Copy trait generates an implicit duplicate of a value by copying its bits

For more information, check the Rust documentation: Trait core::marker::Copy

This means, there is no need to trigger a method, .i.e., .copy() to generate a duplicate value. Meaning, the duplicate happens if you have a regular assignment like:

let duplicate_value = value; 

where duplicate_value variable gets a copy of the values stored in the value variable.

Not All Rust Values Can Copy their own values

One of the most important concepts of Rust is Ownership and Borrowing, which provides memory management different from the traditional garbage collector mechanism.

The ownership and borrowing system makes Rust’s standard behavior to “move” the ownership between the two variables. This is referred as “move semantics”. Take a look at the following example:

#[derive(Debug)]
struct Team {}

fn main() {
   let my_team = Team {};
   let my_duplicate_team = my_team;

   println!("my_team = {my_team:?}, my_duplicate_team = {my_duplicate_team:?}"); 
}

If you try to run the previous code snippet, Rust will throw the following compile error:

error[E0382]: borrow of moved value: my_team

This has to do with Rust’s ownership system. Meaning, my_team has an instance of Team . In other words, my_team is the owner of that particular instance of Team.

However, whenever my_duplicate_team was assigned the values of my_team, what Rust did behind the scenes was to transfer the ownership of the instance of Team stored in my_team. Meaning, the new owner of the instance of Team is my_duplicate_team.

Since my_team no longer owns anything, what Rust’s memory management system does is to remove my_team no matter if you use my_team later on within the same function, which leads to the error previously described at compile time (error[E0382]: borrow of moved value: my_team).

Now, if you have the following code,

fn main() {
    let number1 = 1;
    let number2 = number1;

    println!(
        "number 1 = {}, number 2 = {}",
        number1, number2
    );
}

and attempt to run it, Rust will successfully compile the code and print the values in number1 and number2.

Why didn’t the code fail if number1 transferred ownership to number2 variable for the value of 1?

This is a good assumption, but in this case there is no transfer of ownership. In this scenario, you are seeing the Copy trait in action as it generates a duplicate value by copying the bits of the value 1 stored in number1 . This is referred as “copy semantics”.

Wait a second. How can I know when Rust will implicitly generate a duplicate and when it will implicitly transfer ownership?

By default, Rust implements the Copy trait to certain types of values such as integer numbers, booleans, characters, floating numbers, etc. You can find a list of the types Rust implements the Copy trait by default in here. Below you will see a list of a few of them:

Some of the types implementing the Copy Tait by default

How come Rust implemented the Copy trait in those types by default?

Rust implements the Copy trait in certain types by default as the value generated from those types are the same all the time. Meaning, all integers (12), floating-point numbers (3.4 ), booleans ( true, false ), and characters ('a', 'z') have the same value no matter how many times you use them. Hence, the collection of bits of those Copyable values are the same over time.

Therefore, it is possible to determine what bits to copy to generate a duplicate value. These values have a known fixed size. Fixed-size values are stored on the stack, which is very fast when compared to values stored in the heap. Hence, making the implicit copy a fast and cheap operation of generating duplicate values.

In the next section, you will learn how to implement the Copy trait for those types that are non-Copy by default such as custom structs.

How to Implement the Copy Trait

There are two ways to implement the Copy trait to a struct that doesn’t implement it by default. You will notice that in order to add the Copy trait, the Clone trait must be implemented too.

Use the #[derive] attribute to add Clone and Copy

The most common way to add trait implementations is via the #[derive] attribute. To implement the Copy trait, derive Clone and Copy to a given struct.

#[derive(Clone, Copy)]
struct MyObject {
    my_number: u8,
}

Manually add Copy and Clone implementations to the Struct

Another option available to copy the bits of a value is by manually implementing Copy and Clone to a given struct.

struct MyOtherObject {
    test: u8,
}

impl Copy for MyOtherObject {}

impl Clone for MyOtherObject {
    fn clone(&self) -> MyOtherObject {
        *self
    }
}

How the Clone Trait Works

Similar to the Copy trait, the Clone trait generates a duplicate value. However, the Clone trait is different from the Copy trait in the way it generates the copy.

On one hand, the Copy trait acts as a “shallow” copy. On the other hand, the Clone trait acts as a “deep” copy. Deep copies are generally considered more expensive than shallow copies.

The Clone trait is handy to generate duplicates ofvalues that are stored in the heap. As a reminder, values that don’t have a fixed size are stored in the heap. Some examples are String orVec type values.

How to Implement the Clone Trait

The Clone trait can be implemented in a similar way you implement the Copy trait.

Use the #[derive] attribute to add Clone

To implement the Clone trait, add the Clone trait using the derive attribute in a given struct.

#[derive(Clone)]
struct MyObject {
    my_number: u8,
}

In comparison to the Copy trait, notice how the Clone trait doesn’t depend on implementing other traits.

Manually add a Clone implementation to the Struct

To manually add a Clone implementation, use the keyword impl followed by Clone for <struct> . Then, within curly braces generate a clone function that returns a dereferenced value of the current struct. Below is an example of a manual implementation.

struct MyOtherObject {
    test: u8,
}

impl Clone for MyOtherObject {
    fn clone(&self) -> MyOtherObject {
        *self
    }
}

Comparison Between Copy and Clone Traits

Copy TraitClone Trait
Generates a “shallow” copy / implicit duplicateGenerates a “deep” copy / explicit duplicate
It is faster as it primarily copies the bits of values with known fixed size.It is typically slower when duplicating values stored in the heap.
To implement thecore::marker::Copy trait, the type must have implemented the std::clone::Clone trait.It can be used as long as the type implements the std::clone::Clone trait.
There .copy() method does not exist. The duplicate is implicitly generated, .i.e, assuming x derives the Copy trait:
x = 1;
y = x;

y gets an duplicate ofx .
There is a .clone() method available to the type T if T derives the Clone trait. For instance, assuming x derives the Clone trait:
x = 1;
y = x.clone();

y gets a duplicate of x .

Summary

All in all, this article covered the differences between the Copy and Clone traits whose main purpose is to generate duplicate values.

On one hand, the Copy trait implicitly copies the bits of values with a known fixed size. Hence, there is no need to use a method such as .copy() (in fact, that method doesn’t exist). On the other hand, to use the Clone trait, you must explicitly call the .clone() method to generate a duplicate value.

Did this article help you understand the differences between the Clone and Copy trait?

Share your comments by replying on Twitter of Become A Better Programmer or to my personal Twitter account.