Problem with mutable borrow not being released


#1

Hi,
I need some help understanding why my mutable borrow is not being released.
I’m getting a compile error in my function about not being able to borrow immutable because it’s being borrowed as mutable. However, my understanding is that the mutable borrow should be complete since the function does not return anything and I should be able to borrow immutably again.

Can anyone help clarify this, is my understanding correct? And if not, can you help me understand why the borrow checker thinks the borrow remains active?

Thanks!

Here is the error:

error[E0502]: cannot borrow `cf` as immutable because it is also borrowed as mutable
   --> src/leema/program.rs:146:35
    |
145 |         cf.collect_calls(fix);
    |         -- mutable borrow occurs here
146 | println!("collected calls: {:?}", cf);
    |                                   ^^ immutable borrow occurs here
...
158 |     }
    |     - mutable borrow ends here

error[E0502]: cannot borrow `cf.calls` as immutable because `cf` is also borrowed as mutable
   --> src/leema/program.rs:147:18
    |
145 |         cf.collect_calls(fix);
    |         -- mutable borrow occurs here
146 | println!("collected calls: {:?}", cf);
147 |         for c in cf.calls.iter() {
    |                  ^^^^^^^^ immutable borrow occurs here
...
158 |     }
    |     - mutable borrow ends here

And here is the code:

    pub fn typecheck_current(&mut self, modname: &str, funcname: &str)
    {
        let inter = self.inter.get(modname).unwrap().clone();
        let fix = inter.interfunc.get(funcname).unwrap();
        let mut cf = CallFrame::new(modname, funcname);
        cf.collect_calls(fix);
println!("collected calls: {:?}", cf);
        for c in cf.calls.iter() {
            println!("c: {:?}", c);
            match c {
                &CallOp::LocalCall(ref call_name) => {
                    self.deep_typecheck(modname, call_name);
                }
                &CallOp::ExternalCall(ref extmod, ref extfunc) => {
                    self.deep_typecheck(extmod, extfunc);
                }
            }
        }
    }

The surrounding code is here if that helps: https://github.com/mdg/leema/tree/reload/src/leema


#2

I think it’s because your collect_calls borrows self with 'a, which is same lifetime as CallFrame which means the borrow is live as long as the CallFrame. Why is the lifetime param specified like that in the function?


#3

Ahh, thanks! So there are two questions to why it is specified that way:

  1. I don’t really know what I’m doing w/ lifetimes and borrowing.
  2. I got the following error before adding that and adding that lifetime did make it go away (but also added that other error).
    Here’s the error I was getting:
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/leema/typecheck.rs:91:22
   |
91 |                 self.push_call(CallOp::LocalCall(callname.clone()));
   |                      ^^^^^^^^^

I realize push_call also had a 'a on there. Removing all of them from CallFrame self params fixed it. Thank you so much!


#4

No problem. Yeah, all those 'a lifetime parameters on &self aren’t needed (and are harmful as you can see). Generally, avoid specifying lifetime params and let borrowck’s lifetime elision rules do their thing. You only need explicit lifetime params when you need to specify some relationship between references that is needed for proper lifetime soundness.