Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

cannot borrow as immutable because it is also borrowed as mutable

Tags:

rust

I'm using the structs Foo and Bar from a library and I'm getting a compilation error in the client code. I simplified the code to this:

use std::marker::PhantomData;

struct Foo {
    some_str: &'static str,
}

struct Bar<'a> {
    some_str: &'static str,
    marker: PhantomData<&'a Foo>,
}

impl Foo {
    fn read_data(&self) {
        // add code here
    }
    fn create_bar<'a>(&'a mut self) -> Bar<'a> {
        Bar {
            some_str: "test2",
            marker: PhantomData,
        }
    }
}

fn process(_arr: &mut [Bar]) {}

fn main() {
    let mut foo = Foo { some_str: "test" };
    let mut array: [Bar; 1] = [foo.create_bar()];
    process(&mut array);
    foo.read_data();
}

(playground)

Output:

error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable
  --> src/main.rs:30:5
   |
28 |     let mut array: [Bar; 1] = [foo.create_bar()];
   |                                --- mutable borrow occurs here
29 |     process(&mut array);
30 |     foo.read_data();
   |     ^^^ immutable borrow occurs here
31 | }
   | - mutable borrow ends here

The error in the console output is very clear, but I cannot fix the problem.

like image 528
dmgcodevil Avatar asked Jan 28 '16 06:01

dmgcodevil


People also ask

What is a mutable reference?

A mutable type is a type whose instance data can be modified. The System. Text. StringBuilder class is an example of a mutable reference type. It contains members that can change the value of an instance of the class.

What is borrowing 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.


2 Answers

You can limit the lifetime of the array variable by placing it in a new scope with curly braces ({ ... }):

fn main() {
    let mut foo = Foo { some_str: "test" };
    {
        let mut array: [Bar; 1] = [foo.create_bar()];
        process(&mut array);
    }
    foo.read_data();
}
like image 127
aSpex Avatar answered Sep 29 '22 11:09

aSpex


You original code will work as-is once non-lexical lifetimes are enabled by default:

#![feature(nll)]

use std::marker::PhantomData;

struct Foo {
    some_str: &'static str,
}

struct Bar<'a> {
    some_str: &'static str,
    marker: PhantomData<&'a Foo>,
}

impl Foo {
    fn read_data(&self) {
        // add code here
    }
    fn create_bar<'a>(&'a mut self) -> Bar<'a> {
        Bar {
            some_str: "test2",
            marker: PhantomData,
        }
    }
}

fn process(_arr: &mut [Bar]) {}

fn main() {
    let mut foo = Foo { some_str: "test" };
    let mut array: [Bar; 1] = [foo.create_bar()];
    process(&mut array);
    foo.read_data();
}

With NLL, the borrow checker becomes more advanced and precise; it can now understand that you aren't using array after the call to process so it is safe to use foo in a new manner.

like image 38
Shepmaster Avatar answered Sep 29 '22 12:09

Shepmaster