rustborrow-checker

Borrow checker and &mut borrows for generic params


Why can I not borrow &mut x multiple times when using is_mut_t. But it's fine with is_mut_specific ?

#[cfg(test)]
mod test {
    use std::marker::PhantomData;

    #[test]
    fn test_wc() {
        struct Test<T>{ phantom_data: PhantomData<T>}
        impl <T> Test<T> {
            fn new() -> Self {
                Test { phantom_data: PhantomData }
            }
            fn is_mut_t(&mut self, i: T) {}
            fn is_mut_specific(&mut self, i: &mut i32) {}
        }

        let mut x = 5;
        let mut t: Test<&mut i32> = Test::new();
        
        t.is_mut_specific(&mut x);
        t.is_mut_specific(&mut x);
        // t.is_mut_t(&mut x);
        // t.is_mut_t(&mut x);
    }
}

Solution

  • Let's give names for lifetimes (using imaginary syntax):

    #[cfg(test)]
    mod test {
        use std::marker::PhantomData;
    
        #[test]
        fn test_wc() {
            struct Test<T> { phantom_data: PhantomData<T> }
            impl<T> Test<T> {
                fn new() -> Self {
                    Test { phantom_data: PhantomData }
                }
                fn is_mut_t<'a>(&'a mut self, i: T) {}
                fn is_mut_specific<'b, 'c>(&'b mut self, i: &'c mut i32) {}
            }
    
            let mut x = 5;
            let mut t: Test<&'d mut i32> = Test::new();
            
            (&'e mut t).is_mut_specific::<'e, 'f>(&'f mut x);
            (&'g mut t).is_mut_specific::<'g, 'h>(&'h mut x);
            // (&'i mut t).is_mut_t::<'i>(&'j mut x);
            // (&'k mut t).is_mut_t::<'k>(&'l mut x);
        }
    }
    

    Each occurrence of T is replaced by &'d mut i32. Can you spot the problem?

    When we say &mut i32 in the signature, that gets assigned a generic lifetime that can be any.

    But when we say T, that must be exactly the same as T. And that includes the same lifetime. But if we borrow x for 'd, we cannot borrow it again in 'd, which means at least as long as t exists.