8

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
dmgcodevil
  • 629
  • 1
  • 7
  • 23

2 Answers2

5

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();
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
aSpex
  • 4,790
  • 14
  • 25
  • is it possible to explicitly specify that variable 'foo' lives longer than array variable whithout brackets ? – dmgcodevil Jan 28 '16 at 13:51
  • No, I do not think that's possible. – aSpex Jan 28 '16 at 15:50
  • Looks like there is some borrow checker bug. The `Bar` struct contains immutable reference to the `Foo`, but Rust thinks `foo` borrowed as mutable. – aSpex Jan 28 '16 at 15:55
  • @aSpex `create_bar` takes a `&'a mut self`: that is what is borrowing foo mutably. Even though you're not actually using `self` at all in this example, the `'a` lifetime will be from the assignment to `array` to the moment `array` goes out of scope. With brackets you're limiting the scope of `array` and ending the mutable borrow. There's ongoing work to add "non-lexical lifetimes" that would make Rust smart enough to figure out by itself that `'a` in this case does not actually need to go to the end of the function, as `array` is not *used* after `foo.read_data` – Paolo Falabella Jan 28 '16 at 16:24
  • Thanks for the explanation. But look at this code http://is.gd/Ml2L7G. I am assign mutable reference to immutable reference variable and first variable borrowed as mutable as long as immutable variable still alive. Why? There is no way to modify first variable. – aSpex Jan 28 '16 at 16:41
  • @aSpex what you're is roughly equivalent to [this](http://is.gd/fxmH2R). I agree that Rust is a bit too restrictive here and you're dooing nothing actually unsafe. It's just that Rust does not know how to look at lifetimes with that level of granularity right now – Paolo Falabella Jan 28 '16 at 17:03
  • 1
    @aSpex [limits of lifetimes](https://doc.rust-lang.org/nightly/nomicon/lifetime-mismatch.html) in the Rustonomicon covers this much more in detail – Paolo Falabella Jan 28 '16 at 17:05
4

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366