avatarAnkit Tanna

Summary

The web content discusses the concepts of mutable references in Rust, emphasizing the rules and restrictions enforced by the borrow checker to ensure safe concurrency.

Abstract

The article delves into the intricacies of mutable references in Rust, explaining how they allow for modification of variables, specifically vectors. It highlights the syntax for creating mutable references with &mut and contrasts them with immutable references, which are read-only. The author illustrates the limitations of mutable references, such as having only one mutable reference to a particular piece of data at a time, and the inability to mix mutable and immutable references in the same scope. The Rust compiler's strict enforcement of these rules is justified as a mechanism to prevent data races and ensure memory safety in concurrent programming environments. The article also provides examples of code that compile successfully and those that result in borrow checker errors, demonstrating the practical implications of Rust's borrowing principles.

Opinions

  • The author suggests that mutable references in Rust extend the metaphor of normal references, allowing for modifications to the data they point to, unlike immutable references which are akin to "look but don't touch."
  • The article conveys that the Rust compiler's strictness, particularly around mutable and immutable borrowing, is crucial for avoiding race conditions and maintaining data integrity in concurrent settings.
  • The author expresses that understanding the borrow checker and its error messages is key to working effectively with mutable references in Rust.
  • The author encourages readers to subscribe to a newsletter for more information on Rust and recommends an AI service, ZAI.chat, as a cost-effective alternative to ChatGPT Plus (GPT-4).
The Rust Programming Language

The Rust Programming Language — References and Borrows — Mutable References

Get some mutable references and understand the borrow checker errors!

We’ve seen that we can create mutable variables which allow you to modify the variables. Have a look at the below code snippet:

fn main() {
    let mut years: Vec<i32> = vec![2001, 2002, 2003, 2004];
    let mutable_years: &mut Vec<i32> = &mut years;
}

Observations:

  • mut years means you are able to modify the years variable by adding or removing the elements from the vector.
  • &mut Vec<i32> means its a mutable reference. It means that I want to give the reference to that vec![] and also be able to mutate that vec![]. Normal references, i.e. &Vec<i32> are like look but don’t touch. You can borrow the references but you can’t mutate them.

Mutable references stretch the metaphor for normal references a little bit. It’s like when the parent function lends years reference to you, it expects that there can be certain modifications done on the years. It is not an immutable borrow. For e.g. the .clear() method on a vector is a great example of mutable reference. .clear() is going to get certain elevated levels of permissions on the years variable. It certainly cannot de-allocate the memory assigned for years but it can go ahead and empty out the vector.

fn clear(&mut self) {
  // set self's length to 0
}

fn len(&self) {
  // return length
}

In the definition of .clear() function, it says &mut self rather than &self.

There are some noticeable differences that come up with mutable and immutable references which do not come with let and let mut. There are some restrictions for Mutable References:

  • You can have as many immutable references as you want in a single scope. With mutable references, you can have one mutable references only at a time.

So let’s see the errors we get. First let’s see the below code snippet:

fn main() {
    let mut years: Vec<i32> = vec![2001, 2002, 2003, 2004];
    let mutable_years: &mut Vec<i32> = &mut years;

    let years_ref1: &Vec<i32> = &years;
    let years_ref2: &Vec<i32> = &years;
}

This code snippet does work as I can have unlimited immutable references in a single scope. But for below code snippet:

fn main() {
    let mut years: Vec<i32> = vec![2001, 2002, 2003, 2004];
    let mutable_years: &mut Vec<i32> = &mut years;

    let years2: &mut Vec<i32> = &mut years;
    let years3: &mut Vec<i32> = &mut years;

    years3.clear();
    years2.len();
}
mutable and immutable borrowing

The error says cannot borrow years as mutable more than once at a time. first mutable borrow occures here. second mutable borrow occurs here.

We cannot have mutable and immutable borrows at the same time. See the below code snippet:

fn main() {
    let mut years: Vec<i32> = vec![2001, 2002, 2003, 2004];
    let mutable_years: &mut Vec<i32> = &mut years;

    let years2: &Vec<i32> = years;  // immutable borrow
    let years3: &mut Vec<i32> = &mut years; // murable borrow

    years3.clear();
    years2.len();
}

let years2: &Vec<i32> = years; is immutable borrow where as let years3: &mut Vec<i32> = &mut years; is a mutable borrow. The compiler will throw below error:

mutable and immutable borrowing

The error says that we cannot borrow years as mutable because it is also borrowed as immutable.

When you inverse this line, the compiler again throws the error. Have a look at below code snippet:

fn main() {
    let mut years: Vec<i32> = vec![2001, 2002, 2003, 2004];
    let mutable_years: &mut Vec<i32> = &mut years;

    let years2: &mut Vec<i32> = &mut years;
    let years3: &Vec<i32> = &years;

    years3.clear();
    years2.len();
}

The Rust compiler is very strict about this. You can either have:

  • ONE mutables
  • ANY NUMBER OF immutable

You cannot mix and match with both.

The reason for this strictness is concurrency and errors related to data concurrency. In order to avoid race conditions and get garbage values from the memory. Compiler ensures that at any given point of time only one thread will have a write access to the memory and during that write operation, no other thread will have access to read or write the values from that memory. This way Rust rules out data races issues from the program.

I hope you enjoyed this article about Rust’s Mutable References and understand why Rust’s compiler is so strict.

You can subscribe to my newsletter about The Rust Programming Language here. You can read about all the articles in this series here.

Rust
Rust Programming Language
Web Development
Programming
Performance
Recommended from ReadMedium