Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to satisfy Rust borrow checker with this simple code?

Tags:

rust

I'm getting a Rust compile error from the borrow checker, and don't know how to fix it.
The code below is simple, and no problem with similar code in C++.

fn main() {
    let mut nums = vec![1, 2, 3];
    if let Some(x) = nums.last() {
        nums.push(*x);
    }
}

Here is the error:

message: 'cannot borrow `nums` as mutable because it is also borrowed as immutable (4, 9)'
like image 440
LiLei Avatar asked Jul 27 '17 07:07

LiLei


People also ask

How does the borrow checker work Rust?

The borrow check is Rust's "secret sauce" – it is tasked with enforcing a number of properties: That all variables are initialized before they are used. That you can't move the same value twice. That you can't move a value while it is borrowed.

How do you borrow on Rust?

An Example of Borrowing in Rust You can borrow the ownership of a variable by referencing the owner using the ampersand (&) symbol. Without borrowing by referencing, the program would panic. It would violate the ownership rule that a value can have one owner, and two variables cannot point to the same memory location.

Is Rust better than C++?

You can easily notice similarities between Rust and C++ syntax, but Rust offers a higher level of memory safety without using a garbage collector. Not for the first time, Rust has been named the most loved language—it gained more than 86% of developers' votes.

What does borrowing accomplish in Rust?

Rust supports a concept, borrowing, where the ownership of a value is transferred temporarily to an entity and then returned to the original owner entity. The main function invokes a function print_vector(). A vector is passed as parameter to this function.


2 Answers

When you call .last() you borrow nums as immutable, as mutating it would invalidate the reference x that you hold. You then call .push, which borrows nums as mutable.

The problem is that you now have an immutable and a mutable borrow of the same value at the same time, which is against rusts memory safety guarantees (multiple readers or one single writer guarantee that you will never have invalid memory anywhere).

fn main() {
    let mut nums = vec![1, 2, 3];
    if let Some(x) = nums.last() { // Immutable borrow starts here
        nums.push(*x);             // Mutable borrow starts here
    }                              // Immutable and mutable borrows end here
}

The solution would be to lower the scope of the immutable borrow by immediately dropping the reference of its result, as per @DanielSanchez's example:

let mut nums = vec![1, 2, 3];
if let Some(&x) = nums.last() { // Immutable borrow starts and ends here
    nums.push(x);               // Mutable borrow starts here
}                               // Mutable borrow ends here
like image 53
Jan Hohenheim Avatar answered Oct 13 '22 00:10

Jan Hohenheim


You can dereference the reference in the guard clause:

fn main() {
    let mut nums = vec![1, 2, 3];
    if let Some(&x) = nums.last() {
        nums.push(x);
    }
}

Rust has a powerful pattern matching feature, you can unpack almost everything if you know its type. Check the Rust pattern matching documentation.

like image 28
Netwave Avatar answered Oct 13 '22 00:10

Netwave