J Cooper
J Cooper

Reputation: 17062

Storing mutable references and lifetimes

I've now spent a number of fruitless hours trying to get this to work, but I'm afraid my only recourse now is to try and summarize my code and ask you all for help.

The situation is, if I uncomment the two instances of mut in the following code, it no longer compiles (and not for a reason that makes sense to me):

use std::cell::RefCell;
use std::ops::Index;

struct Foo<'a, T: 'a> {
    t_array: [T; 3],
    t_refs: RefCell<Vec<&'a /* mut */ T>>, //'           // 1
}
impl<'a, T> Foo<'a, T> {
    fn new(t1: T, t2: T, t3: T) -> Foo<'a, T> { //'
        Foo {
            t_array: [t1, t2, t3],
            t_refs: RefCell::new(Vec::with_capacity(3))
        }
    }
    fn add_t_ref(&'a mut self, at_index: usize) { //'
        let t_ref = & /* mut */ self.t_array[at_index];  // 2
        let mut t_refs = self.t_refs.borrow_mut();
        t_refs.push(t_ref);
    }
}
impl<'a, T> Index<usize> for Foo<'a, T> {
    type Output = T;
    #[inline]
    fn index(&self, index: &usize) -> &T {
        let t_refs = self.t_refs.borrow();
        t_refs[*index]
    }
}

The error that occurs when instead storing mut refs in the Vec is:

blah3_mut.rs:26:9: 26:15 error: `t_refs` does not live long enough
blah3_mut.rs:26         t_refs[*index]
                        ^~~~~~
blah3_mut.rs:24:42: 27:6 note: reference must be valid for the anonymous lifetime #1 defined on the block at 24:41...
blah3_mut.rs:25:42: 27:6 note: ...but borrowed value is only valid for the block suffix following statement 0 at 25:41

I would love it if someone could explain why this is happening; intuitively it seems like it shouldn't be an issue that the RefCell's borrow goes out of scope. The Vec (and thus the RefCell too) do not own the data pointed to by the reference, so why does the compiler care about the lifetime of their references?

P.S. I know my simplified code excerpt doesn't make it obvious why I'm storing mutable references in a Vec or why I'm using a RefCell—suffice to say it's not by accident though

P.P.S. I tried messing around a bit with lifetime annotations on the index method and/or the trait's associated type, but thus far only managed to get different errors from doing that

Upvotes: 0

Views: 430

Answers (1)

Shepmaster
Shepmaster

Reputation: 431939

The reason it works with immutable references is because those can be implicitly copied. When you switch to trying to return a &mut, then there would be two places that have that reference - the Vec and the caller of your function. This would introduce aliasing, and mutable aliases are not allowed in Rust.

You can even ignore the Index implementation and just try this:

fn main() {
    let mut f = Foo::new(1,2,3);
    f.add_t_ref(1);
    f.add_t_ref(2);
}

You'll get:

error: cannot borrow `f` as mutable more than once at a time

While all of the above is true, it doesn't really explain why you get the particular error messages you do.

Upvotes: 2

Related Questions