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.