Mutable and immutable borrow and lifetime parameters?


#1

I’m writing a parser that stores the Strings comprising the tokens as &'input str. This has been working well up until I tried to store parsed AST in an environment for future lookup. When I do that, I don’t seem to be able to resolve the borrow issues.

https://play.rust-lang.org/?gist=73ed8e4c8455b7583d78&version=stable is a simplified version of what I’m trying that shows the same issues. It appears that storing the Strings in a Vec to provide a long enough lifetime isn’t sufficient. The errors I get are:

<anon>:51:35: 51:39 error: cannot move out of borrowed content [E0507]
<anon>:51                 env.push(Box::new(*val));
                                            ^~~~
<anon>:44:11: 44:18 error: cannot borrow `history` as mutable because it is also borrowed as immutable [E0502]
<anon>:44           history.push(line);
                    ^~~~~~~
<anon>:46:18: 46:25 note: previous borrow of `history` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `history` until the borrow ends
<anon>:46         let l = &history.last().unwrap()[..];
                           ^~~~~~~
<anon>:62:4: 62:4 note: previous borrow ends here
<anon>:36   {
...
<anon>:62   }
            ^
error: aborting due to 2 previous errors
playpen: application terminated with error code 101

Is there a way to workaround what I’m trying to do? Or do I need to move to something different?


#2

This cannot work. You’re trying to keep pointers to something you’re also mutating. The compiler has no way of proving that mutating history isn’t going to invalidate previous borrows.

The simplest solution is to change Val to not borrow the data.

If you want to keep flexibility, you can change the structure to use Cow internally, and provide a method to recursively take ownership of its contents, but whether that’s going to be of benefit depends on what you’re actually doing.

As an aside, you have way too many Boxes in this example. I removed every single Box, and it didn’t change the semantics one bit.


#3

OK, I wasn’t sure if there was a way to tell the compiler I’m not mutating the contents of the strings in the Vec but only adding to it. Thanks.


#4

The strings do not have their own lifetime; they have the same lifetime as the thing that owns them, which is the vector. As far as the compiler is concerned, mutating the vector is indistinguishable from transitively mutating everything it owns.