Copying values, pt. 1

In the previous chapter we introduced ownership and borrowing.
We stated, in particular, that:

  • Every value in Rust has a single owner at any given time.
  • When a function takes ownership of a value ("it consumes it"), the caller can't use that value anymore.

These restrictions can be somewhat limiting.
Sometimes we might have to call a function that takes ownership of a value, but we still need to use that value afterward.

fn consumer(s: String) { /* */ }

fn example() {
     let mut s = String::from("hello");
     consumer(s);
     s.push_str(", world!"); // error: value borrowed here after move
}

That's where Clone comes in.

Clone

Clone is a trait defined in Rust's standard library:

pub trait Clone {
    fn clone(&self) -> Self;
}

Its method, clone, takes a reference to self and returns a new owned instance of the same type.

In action

Going back to the example above, we can use clone to create a new String instance before calling consumer:

fn consumer(s: String) { /* */ }

fn example() {
     let mut s = String::from("hello");
     let t = s.clone();
     consumer(t);
     s.push_str(", world!"); // no error
}

Instead of giving ownership of s to consumer, we create a new String (by cloning s) and give that to consumer instead.
s remains valid and usable after the call to consumer.

In memory

Let's look at what happened in memory in the example above. When let mut s = String::from("hello"); is executed, the memory looks like this:

                    s
      +---------+--------+----------+
Stack | pointer | length | capacity | 
      |  |      |   5    |    5     |
      +--|------+--------+----------+
         |
         |
         v
       +---+---+---+---+---+
Heap:  | H | e | l | l | o |
       +---+---+---+---+---+

When let t = s.clone() is executed, a whole new region is allocated on the heap to store a copy of the data:

                    s                                    t
      +---------+--------+----------+      +---------+--------+----------+
Stack | pointer | length | capacity |      | pointer | length | capacity |
      |  |      |   5    |    5     |      |  |      |   5    |    5     |
      +--|------+--------+----------+      +--|------+--------+----------+
         |                                    |
         |                                    |
         v                                    v
       +---+---+---+---+---+                +---+---+---+---+---+
Heap:  | H | e | l | l | o |                | H | e | l | l | o |
       +---+---+---+---+---+                +---+---+---+---+---+

If you're coming from a language like Java, you can think of clone as a way to create a deep copy of an object.

Implementing Clone

To make a type Clone-able, we have to implement the Clone trait for it.
You almost always implement Clone by deriving it:

#[derive(Clone)]
struct MyType {
    // fields
}

The compiler implements Clone for MyType as you would expect: it clones each field of MyType individually and then constructs a new MyType instance using the cloned fields.
Remember that you can use cargo expand (or your IDE) to explore the code generated by derive macros.

Exercise

The exercise for this section is located in 04_traits/11_clone