rustborrow-checker

Rust Borrow Checker Issues


I need some help figuring out the Rust borrow checker. The following is a minimal example of the problem I'm facing:

struct VecMutRef<'a> {
    vec: &'a mut Vec<usize>,
}

trait Length {
    fn len(&self) -> usize;
}

impl<'a> Length for VecMutRef<'a> {
    fn len(&self) -> usize {
        self.vec.len()
    }
}

fn f<'a>(v: &'a mut Vec<usize>) -> usize
where
    VecMutRef<'a>: Length,
{
    let first = g(v);
    let second = g(v);
    first + second
}

fn g<'a>(v: &'a mut Vec<usize>) -> usize
where
    VecMutRef<'a>: Length,
{
    let vec = VecMutRef { vec: v };
    vec.len()
}

The compiler complains that I cannot borrow *v twice in the function f. I was thinking by the time the first call to the function g is returned, it's done with the mutable reference to v, so I'm free to mutably borrow *v again. But apparently, that's not the case. It would be great if someone could explain to me what's wrong and suggest a possible workaround. I understand that in this particular example, I don't need a mutable reference to Vec just to get its length. The example is just for an illustrative purpose.


Solution

  • This is not a bug in the compiler, the compiler does exactly what it was requested here.

    g() can, theoretically, use v for its whole lifetime ('a) since it is given a reference that long.

    Normally, we would give g() a newer lifetime, shorter than f()'s 'a, but here we can't do that, since VecMutRef is only known to implement Length (which g() requires) for 'a. So we end up passing v with lifetime 'a twice, which is invalid.

    If it is difficult to follow the above explanation, here is an example that will hopefully make it clearer:

    use std::sync::Mutex;
    
    struct VecMutRef<'a> {
        vec: &'a mut Vec<usize>,
    }
    
    trait Length {
        fn evil(self);
    }
    
    static ALL: Mutex<Vec<&'static mut Vec<usize>>> = Mutex::new(Vec::new());
    
    impl Length for VecMutRef<'static> {
        fn evil(self) {
            ALL.lock().unwrap().push(self.vec);
        }
    }
    
    fn f<'a>(v: &'a mut Vec<usize>) -> usize
    where
        VecMutRef<'a>: Length,
    {
        let first = g(v);
        let second = g(v);
        first + second
    }
    
    fn g<'a>(v: &'a mut Vec<usize>) -> usize
    where
        VecMutRef<'a>: Length,
    {
        VecMutRef { vec: v }.evil();
        0
    }
    
    fn main() {
        f(Box::leak(Box::new(vec![1, 2, 3])));
    }
    

    The code was changed here, but the important part is that f() and g() signatures weren't changed at all. That means that if your code would compile, this would have to compile too. But this code is clearly invalid - it creates two duplicated &mut references in ALL.

    A higher-ranked lifetime (for<'a> VecMutRef<'a>: Length) is the correct solution here. If it does not work, you can open a new question with your concrete use-case.