#Borrowing error after moving code into a function

1 messages · Page 1 of 1 (latest)

slim forum
#

I have this code, where a method that takes &mut self has this in the beginning:

    fn run_instruction(&mut self) -> Result<(), RuntimeError> {
        let last_frame = self
            .stack_frames
            .last_mut()
            .expect("tried to run without any stack frames");

        let function: &Function = match last_frame.function {
            FunctionRef::MainFunction => &self.program.main_function,
            FunctionRef::Function(i) => &self.program.functions[i]
        };

Those last 4 lines - turned out I needed similar behavior in other methods, so I decided to put that match expression into its own method and re-use that:

    fn get_function(&self, function_ref: &FunctionRef) -> &Function {
        match function_ref {
            FunctionRef::MainFunction => &self.program.main_function,
            FunctionRef::Function(i) => &self.program.functions[*i]
        }
    }

//////////////

        let last_frame = self
            .stack_frames
            .last_mut()
            .expect("tried to run without any stack frames");

        let function: &Function = self.get_function(&last_frame.function);

#

But after I did this, I got this error:

error[E0502]: cannot borrow `*self` as immutable because it is also borrowed as mutable
   --> src\vm\mod.rs:310:35
    |
305 |           let last_frame = self
    |  __________________________-
306 | |             .stack_frames
307 | |             .last_mut()
    | |_______________________- mutable borrow occurs here
...
310 |           let function: &Function = self.get_function(&last_frame.function);
    |                                     ^^^^^^^^^^^^^^^^^^--------------------^
    |                                     |                 |
    |                                     |                 mutable borrow later used here
    |                                     immutable borrow occurs here

How can this be? The function contains the exact same code as the snippet before, and yet it only works when it's used like in the first snippet. When I move the same code into a function, suddenly there's a borrowing error, even though the code is functionally the same.
If it's not possible to have this code inside a function, then so be it, I'll just copy-paste it in all the places I need it. But I want to understand why this doesn't compile.

#

And yes, I tried changing get_function to use a &mut self, but then I got an error of "cannot borrow *self as mutable more than once at a time"

eternal rain
#

even though get_function only borrows self.program, it couldn't be seen from the outside of function and it would assume that the whole self is being borrowed, colliding with the existing mutable borrowing of self.stack_frames.

#

it seems that you don't strictly need to pass function_ref as a reference here, passing it as a value should help you (but it may not work if you have more code below)

slim forum
eternal rain
#

do you make use of last_frame below that code? then it indeed won't work.

slim forum
#

I do

eternal rain
#

otherwise I think NLL should take care of finishing the lifetime before get_function

slim forum
#

what's that

eternal rain
#

lifetime typically lasts from a specific statement to the end of block, but non-lexical lifetime (NLL) may shorten it to end before some other statement

#

but in this case you do refer to last_frame later so NLL can't shorten it to make it compile

#

anyway... this kind of lifetime problem usually happens because one struct contains too many things

#

one way to solve this is to move get_function into self.program

#

so the original function would still see self.program and only borrow it

slim forum
#

took me a while to figure out, but that's exactly what I did!