3.4 KiB
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 s
+---------+--------+----------+ +---------+--------+----------+
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.
References
- The exercise for this section is located in
exercises/04_traits/10_clone