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