100-exercises-to-learn-rust/book/src/04_traits/10_clone.md

112 lines
3.4 KiB
Markdown
Raw Normal View History

2024-05-13 04:21:03 +08:00
# 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.
```rust
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:
```rust
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`:
```rust
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:
```text
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:
```text
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:
```rust
#[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`