Rust Borrow Checker Explained Part 1: Copying & Moving
Tags: rust, tutorial
Published: 2023-11-05 04:18:24

## Intro

One way to avoid borrowing completely is to make copies, which is the focus of this article:
* How memory is managed in Rust using the "stack".
* How the `Copy` marker trait is used to denote "trivial" types that are implicitly copied.
* How the `Clone` trait allows you to avoid borrowing completely, but at the expense if copying data around.
* "Moving" rules.

## Stack Memory Management

All memory is managed by a stack per thread in Rust. When calling functions, the arguments are copied onto the stack. During execution of the function, any local variables are pushed onto the stack, too. Finally, when the function returns, any local variables and function arguments are popped off the stack and replaced with the return value(s) of the function.

```rust
fn main() {
    let num = 2;
    let even = is_even(num);
    print!("is_even={even} num={num}")
}

fn is_even(num: i32) -> bool {
    num % 2 == 0
}
```
[Try it!](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=433165cf3591a1b6290ecfd3d7178af7)

In this simple example, `main` is called with no arguments, so the stack is empty. Technically this isn't entirely true, since a "program counter" is always recorded into the stack on every function call so the code knows where to resume execution on return, but we will gloss over that detail for now.

The first line of code in `main` pushes a local variable `num` onto this stack and assigns it the value `2`. So, now we have a stack of one value: `[2]`.

TODO: Display a stack visual

The second line of code copies `num` onto the stack and calls `is_even`. So, now our stack has two values: `[2, 2]`.

TODO: Display a stack visual

The `is_even` function checks if the top of the stack is an even value, and then returns this fact. So, at the end of this function, the stack is now `[2, true]` (the `2` function argument gets popped and the function result `true` is pushed).

TODO: Display a stack visual

The result of the function is stored in a local variable `even`, and so the stack continues to contain `[2, true]` after the second line completes.

TODO: Display a stack visual

The final line of code prints both the local variables stored on the stack.

When `main` returns, the two local variables are popped off the stack.

TODO: Display a stack visual

## The `Copy` trait

In our first example, we worked with the `i32` type. Let's replace this with our own type the wraps the `i32` and see what happens:

```rust
#[derive(Debug)]
struct Num(i32);

fn main() {
    let num = Num(2);
    let even = is_even(num);
    print!("is_even={even:?} num={num:?}")
}

fn is_even(num: Num) -> bool {
    num.0 % 2 == 0
}
```
[Try it!](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=1f5b4c14547adce39e7ce6c0a4c33641)

The `#[derive(Debug)]` and extra `:?`s in the `print!` statement are to avoid implementing the `Display` trait, you can ignore that for now.

Overall, this is the same code as our first example, but the `i32` is wrapped into a `Num` struct... but the compiler gives us this error:

```
error[E0382]: borrow of moved value: `num`
  --> src/main.rs:7:34
```

Note that this isn't the entire error message, since the Rust compiler tries very hard to give you more useful information on how to fix the problem.

So, what is different about the `Num` struct vs the `i32`? The difference is that `i32` "implements" the `Copy` trait. So, let's change `Num` so it implements the `Copy` trait and see if it compiles:

```rust
// Notice the "Clone" is also derived
#[derive(Debug, Clone)]
struct Num(i32);

// Notice the "Copy" implementation:
impl Copy for Num {}

fn main() {
    let num = Num(2);
    let even = is_even(num);
    print!("is_even={even:?} num={num:?}")
}

fn is_even(num: Num) -> bool {
    num.0 % 2 == 0
}
```
[Try it!](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=943c81c709a428143f86cae9d413082d)

The `Copy` trait is a "marker" trait and thus it has no implementation body. In other languages this pattern of implementing a trait which has no code associated with it, might be called a "marker interface" or "tagging interface". 

Therefore, the `impl Copy for Num {}` is all that is needed to implement this trait. Note that this trait requires the `Clone` trait to be implemented, so I added that to the `#[derive(Debug, Clone)]`.

Another way to implement both `Clone` and `Copy` is to derive this using: `#[derive(Debug, Copy)]`.

Let's make one small change to the `Num` struct so it has an English string that describes the number:

```rust
// Notice the extra `String` field:
#[derive(Debug, Clone)]
struct Num(i32, String);

impl Copy for Num {}

fn main() {
    let num = Num(2, "two".into());
    let even = is_even(num);
    print!("is_even={even:?} num={num:?}")
}

fn is_even(num: Num) -> bool {
    num.0 % 2 == 0
}
```
[Try it!](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=c18a7166c1e6ecafeb10202049fd4fe9)

Now we get this error:

```
error[E0204]: the trait `Copy` cannot be implemented for this type
 --> src/main.rs:4:15
```

This is because **all** fields of the struct must implement `Copy`, but the `String` type does not implement this.

Although it would be handy if `String` implemented `Copy`, it is also a potentially computationally expensive operation, since a `String` is not a fixed size. Imagine if you had a million character string that you passed around. Every time it was passed into a function, your program would need to copy the million characters.

However, for simple types (like enums with no associated values), it is highly recommended that you just `#[derive(Copy)]`.

## Cloning

One way to solve the problem of structures that can't implement `Copy` is to explicitly `clone()` them.

```rust
// Notice the Clone is derived and NOT Copy
#[derive(Debug, Clone)]
struct Num(i32, String);

fn main() {
    let num = Num(2, "two".into());
    // Notice the call to `.clone()`
    let even = is_even(num.clone());
    print!("is_even={even:?} num={num:?}")
}

fn is_even(num: Num) -> bool {
    num.0 % 2 == 0
}
```
[Try it!](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=1ab53cc4fea8d66e29a192a0f2c0c035)

Although this is a great "hammer" to have in your toolbox, you do need to be aware of the performance implications in case the thing being copied around is large.

Cloning is also not great for any containers that are managing resources. For example, you probably shouldn't be `clone()`ing a file descriptor or a mutex.

To manage resources, it is recommended to implement the `Drop` trait so that resources can automatically be "closed" just before their memory is released. The `Drop` trait is analogous to implementing Java's `AutoCloseable` interface, and using a `try-with-resource` block to ensure the resource is "closed", or Golang's `defer` statement. 

## Moving (aka shallow copy)

All datatypes in Rust can be "moved", which is just a fancy name for taking a shallow bit-for-bit copy of a datatype. Moving does come with a big restriction though: you can't continue to use the value, and thus in this final example, the `is_even()` function was modified so it returns the `Num` that was passed into it.

```rust
#[derive(Debug)]
struct Num(i32, String);

fn main() {
    let num = Num(2, "two".into());
    // num is "moved" in here:
    let (even, num) = is_even(num);
    // ...which means we can NOT print it any more
    print!("is_even={even:?} num={num:?}")
}

fn is_even(num: Num) -> (bool, Num) {
    // num is "moved" out here:
    (num.0 % 2 == 0, num)
}
```
[Try it!](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e3bfc3672ac6f1f441e239279883fc2d)

If you try to remove the re-assignment to num:

```rust
#[derive(Debug)]
struct Num(i32, String);

fn main() {
    let num = Num(2, "two".into());
    // notice that we no longer re-assign to num!
    let (even, _) = is_even(num);
    // ...which means we can NOT print it any more
    print!("is_even={even:?} num={num:?}")
}

fn is_even(num: Num) -> (bool, Num) {
    // num is "moved" out here:
    (num.0 % 2 == 0, num)
}
```

You will get this error:

```
error[E0382]: borrow of moved value: `num`
  --> src/main.rs:9:34
```

This is because line 7 is "stealing" the `num`, and thus all subsequent attempts to use `num` (like when it performs "print") will fail.

Rust enforces this constraint, since a shallow copy could be stored in a global variable, but there is now multiple shallow copies that are potentially sharing data, since the "deep" parts of that data did not get copied. How would the borrow checker know when the "deep" parts of the data are no longer used?

## Recursive Data Structures: `Box`

In order for the compiler to know how much space is needed to store a structure on the stack, it needs to know the exact size of that structure. Therefore if you try to define a recursive data structure like this:

```rust
#[derive(Debug)]
struct Num(i32, Option<Num>);

fn main() {
    let num = Num(2, None);
    print!("num={num:?}")
}
```
[Try it!](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=7c2beb00d963077a322625d2c2c6cf61)

You will get this error:
```
error[E0072]: recursive type `Num` has infinite size
 --> src/main.rs:2:1
```

To get around this limitation, you instead need to have the memory stored on the "heap", and instead just have a unique pointer stored on the stack. You can use the [`Box`](https://doc.rust-lang.org/std/boxed/struct.Box.html) datatype in Rust to do this.

Let's try to modify the recursive data structure by using a `Box`:

```rust
#[derive(Debug)]
struct Num(i32, Option<Box<Num>>);

fn main() {
    let num = Num(2, None);
    print!("num={num:?}")
}
```
[Try it!](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=7c2beb00d963077a322625d2c2c6cf61)

Note that all the same ownership constraints are the same for a `Box`. However, a `Box`s size doesn't need to be known at compile time since it dynamically allocates memory on the heap. Recursive data structures are just one example of where this is needed, but this is also needed when working with `dyn` traits, but this series won't get into the details of that.

## Copy & Clone Recap

To recap:
* Simple types (in Java these are called "primitives") should implement `Copy` in order to get implicit copy semantics.
* Other types, should implement `Clone`, and you can use the `.clone()` "hammer" to avoid borrowing. Notice how using this "hammer" is very explicit in our code, since it requires calling `.clone()`.
* Any non-memory resource should **not** implement `Clone` and should instead implement the `Drop` trait to have the resource automatically released.
* Rust allows "moving" values to make shallow clones, and if you use this, you will need to return the "moved" value if you want to subsequently make use of it.

To learn how to actually borrow values, please go to [Part 2](https://dev.to/brimworks/rust-borrow-checker-explained-part-2-borrowing-4036)