Rust Borrow Checker Explained Part 2: Borrowing
Tags: rust, tutorial
Published: 2023-11-11 21:13:28

## Intro

In our last article we discussed how to avoid the borrow checker by making copies. In this article, I will introduce the concept of borrowing which allows us to avoid copying and thus increases the performance of our code.

More specifically, we will see:
* How to perform an immutable borrow using `&`.
* How to perform a mutable borrow using `&mut`.
* How the borrow check "Angel" will catch errors and keep our code bug free.
* How to use `*` to unwrap the borrow.

## Borrowing

It would be a lot more efficient if you could just pass in a pointer instead of copying data around. This is exactly what borrowing is in Rust.

In the first part of this series, we tried to compile this code:

```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)

...and we saw the `borrow of moved value` error, but if you read a bit further in the error, you would see this:

```
consider changing this parameter type in function `is_even`
to borrow instead if owning the value isn't necessary
```

This suggestion is a better solution than using `clone()`, since we can avoid copying data.

To borrow in Rust, you use `&` in front of the thing you are borrowing, so we change `num.clone()` to `&num`. We also need to declare our `is_even()` function to take a borrowed `&Num` type by adding that `&` in front again:

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

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=3aa5d277ef7cd1a91c24c6c577c16f01)

Under the covers, instead of copying the entire `Num(2, "two")` structure onto the stack, a pointer to this structure is placed on the stack, and we avoid copying.

## Mutable Borrows

Let's say we want our `is_even()` function to have a side-effect of modifying the `String` associated with the `Num` borrowed in. Now that we are simply pushing a pointer onto the stack, we should be able to produce a side-effect:

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

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 {
    let even = num.0 % 2 == 0;
    num.1 = if even { "even".into() } else { "odd".into() };
    return even;
}
```
[Try it!](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=587851b0ce99efeba8cc600ef2391b6a)

...but the compiler is angry again:

```
error[E0594]: cannot assign to `num.1`, which is behind a `&` reference
  --> src/main.rs:12:5
```

This is because everything is immutable by default in Rust. To get over this, we need to declare our references to be mutable, and we need the `num` in main to also be declared `mut`:

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

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

fn is_even(num: &mut Num) -> bool {
    let even = num.0 % 2 == 0;
    num.1 = if even { "even".into() } else { "odd".into() };
    return even;
}
```
[Try it!](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=c87955a6ae0dfb6bb336b530ca04f6bf)

Easy!

## The Borrow Check Angel

Let's switch it up and try implementing a function that splits a string on `:`.

### Copying Split

One straight forward way to implement this is to take the owned `String` and return an owned `String`:

```rust
fn main() {
    let key = split_key("left: right".into());
    print!("key={key}");
}

fn split_key(str: String) -> String {
    if let Some(colon) = str.find(':') {
        return str[0..colon].into();
    }
    return "".into();
}
```
[Try it!](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=de5982a2dac6a2889a468c48e22ed23a)

However, this has a bunch of copies when performing conversions using `.into()`.

### Borrowing Split

It would be more efficient, if we borrowed the string we split on... and since the result is a substring of the input, we could also have the return type be borrowed too:

```rust
fn main() {
    // Avoid .into() since "string" type is &str by default:
    let key = split_key("left: right");
    print!("key={key}");
}
// param is now &str, and result is &str:
fn split_key(str: &str) -> &str {
    if let Some(colon) = str.find(':') {
        // Avoid .into(), and use `&` to borrow the result:
        return &str[0..colon];
    }
    // Avoid .into()
    return "";
}
```
[Try it!](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=bcb704f2179539438d1663570d230fd7)

This implementation completely removes all the string copies from the previous implementation.

### Add Parens To Split Result

Requirements changed, and we now want square braces to always be added to the result of `split_key()`. The `format!` macro allows us to easily add the square braces. The first argument is our format string `"[{}]"`, the `{}` is a place holder for the `&str[0..colon]` argument, and then we borrow the result:

```rust
fn main() {
    let key = split_key("left: right");
    print!("key={key}");
}

fn split_key(str: &str) -> &str {
    if let Some(colon) = str.find(':') {
        return &format!("[{}]", &str[0..colon]);
    }
    return "";
}
```
[Try it!](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=687fb0143be4f1139f21f9e7407769c2)

The borrow check **Angel** points out the bug:

```
error[E0515]: cannot return reference to temporary value
 --> src/main.rs:9:16
```

The result of `format!` is an "owned" `String` which is a stack local variable, and so we can't just borrow it and return the result.  If the compiler did not catch this, then we would have a "dangling" pointer which would be invalid after the `split_key()` function returns.

To fix this, we need to change the function so it returns a `String` type:

```rust
fn main() {
    let key = split_key("left: right");
    print!("key={key}");
}

fn split_key(str: &str) -> String {
    if let Some(colon) = str.find(':') {
        return format!("[{}]", &str[0..colon]);
    }
    return "".into();
}
```

## Mutable Borrow Exclusivity Rule

One more rule to be aware of is that there can only be a single mutable borrowed value. Let's say that we want to write code that modifies a vector of numbers so that all numbers are odd.

```rust
fn main() {
    let mut vec = vec![1, 2, 3];
    
    for (idx, num) in vec.iter().enumerate() {
        if num % 2 == 0 {
            vec[idx] = num + 1;
        }
    }
}
```
[Try it!](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=5927d6ddf8d4f3a13fd0e1cdb33078e3)

The borrow checking Angel helped us out again:

```
error[E0502]: cannot borrow `vec` as mutable because it is
also borrowed as immutable
 --> src/main.rs:6:13
```

In Java iterating over a collection and mutating at it the same time, could result in a [ConcurrentModificationException](https://docs.oracle.com/javase/8/docs/api/java/util/ConcurrentModificationException.html) at runtime.

To fix this, we just need to use `iter_mut()` instead which iterates over the `Vec` elements as a `&mut` element type. The `*` syntax is needed to unwrap the borrow:

```rust
fn main() {
    let mut vec = vec![1, 2, 3];
    
    for num in vec.iter_mut() {
        if *num % 2 == 0 {
            *num = *num + 1;
        }
    }
    print!("vec={vec:?}")
}
```
[Try it!](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=da43cf47fa128fd3ad84f17bd5d970df)

## Borrowing Recap

To recap, we can increase our code performance by borrowing, which under the covers is just a fancy name for a pointer. There are two variations on borrowing, an immutable borrow using `&` syntax and a mutable borrow which uses the `&mut` syntax. Mutable borrows are exclusive, so no other borrowing of the same instance is allowed.

We also saw how the "borrow checker" is actually very helpful in pointing out when you attempt to use a borrowed value outside of the bounds of the "owned" objects lifetime.

So far, the lifetimes of all of our borrows have been implicit, but in [Part 3](https://dev.to/brimworks/rust-borrow-checker-explained-part-3-function-result-lifetimes-20j6) we will discuss how to explicitly declare lifetimes.