Can't get out of E0502 error


#1

Hi all,

I have error E0502 , and even reading others posts, I can’t figure out how get out of it.

https://is.gd/m9xdgV

struct A {
    list: Vec<i16>,
}

impl A {
    fn f1(&self) -> Vec<(usize,&i16)>
    {
        // search for indices matching predicate. Return is a vector of tuples
        let result: Vec<_> = self.list.iter().enumerate().filter(|e| e.1%2 == 0).collect();
        result
    } 

    fn f2(&mut self) {
        let result = { self.f1() };
        
        self.list.remove(0);
    }
    

}

fn main() {
    let mut a = A { list: Vec::new() };
    
    a.list = vec![1,2,3,4,5,6,7,8,9];
    
    let result = a.f1();
    println!("{:?}", result);
    

}

I understand the cause, but can’t fix it. I thought putting the let result in a scope {}, the error will disappear. I thought the borrowing will end after the scope…

rustc 1.15.1 (021bd294c 2017-02-08)
error[E0502]: cannot borrow `self.list` as mutable because `*self` is also borrowed as immutable
  --> <anon>:16:9
   |
14 |         let result = { self.f1() };
   |                        ---- immutable borrow occurs here
15 |         
16 |         self.list.remove(0);
   |         ^^^^^^^^^ mutable borrow occurs here
17 |     }
   |     - immutable borrow ends here.

Any hint?

Thanks a lot.


#2

Your result vector contains &i16 references to the original self.list. If you were allowed to remove(0), that would invalidate any reference to self.list[0], even though with an omniscient view we can tell that one doesn’t match your predicate. But worse, the removal would shift all of the remaining items to the left, so every reference is now bad.

IOW, you’ve got a gun aimed at your foot, and Rust rightly stopped you from pulling the trigger.

The actual non-elided signature of your function is fn f1<'a>(&'a self) -> Vec<(usize,&'a i16)>. That is, the lifetime of that vector extends the original borrow from self. You won’t be able to do anything mutable with self until that vector is gone.

Since these are just i16, you could just copy them out, like self.list.iter().cloned()[etc], then the resulting vector will not have a borrowed lifetime anymore.


#3

What’s keeping the borrow alive is the &i16 returned by f1, because it’s pointing at the element inside the vector. The separate { } scope doesn’t matter because you’re passing the result outside of it.

You should just return a (usize, i16) instead. There’s nothing you can do with an &i16 that you can’t do with an i16 as well.


#4

Focusing on this - you didn’t actually put the let result in the scope, just the expression on the right side. It would be fine if you used: { let result = self.f1(); ... } self.list.remove(0);