Optional function argument captures lifetime if created via as_ref

I cannot figure out, why the following code doesn't compile (Rust Playground):

pub fn main(old: &mut Option<i32>) {
    let neew = MaybeChanged::new(old, Some(42));
    //let sub = MaybeChanged::new(&old, &neew.data);
    let sub = MaybeChanged::new(&old.as_ref(), neew.data.as_ref());
    
    *old = None;
    drop(sub)
}

pub struct MaybeChanged<T> {
    pub data: T,
    pub is_changed: bool,
}

impl<T> MaybeChanged<T> {
    fn new<U: PartialEq<T>>(old: &U, new: T) -> Self {
        Self {
            is_changed: old == &new,
            data: new,
        }
    }
}

error[E0506]: cannot assign to `*old` because it is borrowed
 --> src/lib.rs:6:5
  |
4 |     let sub = MaybeChanged::new(&old.as_ref(), neew.data.as_ref());
  |                                  ------------ `*old` is borrowed here
5 |     
6 |     *old = None;
  |     ^^^^^^^^^^^ `*old` is assigned to here but it was already borrowed
7 |     drop(sub)
  |          --- borrow later used here

If I use Line 3 instead of Line 4, everything works fine. How can it be, that the lifetime of old is captured, when the the first param in MaybeChanged::new has nothing to do with it's output?

Simplified as follows: Rust Playground

#[test]
fn success() {
    let mut a = Some(String::new());
    let ref_a = &mut a;
    {
        let b = Some(String::new());
        eq(ref_a.as_ref(), b.as_ref()); // '1: U=Option<&'1 _>, T=Option<&'1 _>
        // '1 ends here
        *ref_a = None; // no overlapping
    }
}
#[test]
fn error() {
    let mut a = Some(String::new());
    let ref_a = &mut a;
    {
        let b = Some(String::new());
        // (&mut Option<_>).as_ref() => (&'2 Option<_>).as_ref() => Option<_>::as_ref(&'2 Option<_>) -> Option<'2 _>
        let c = eq(ref_a.as_ref(), b.as_ref()); // '2: U=Option<&'2 _>, T=Option<&'2 _>
        *ref_a = None; // '2 must ends here
        c; // but '2 is required to end here by returning T: lifetime overlapping
    }
}

// impl<T> PartialEq<Option<T>> for Option<T> where T: PartialEq<T>
fn eq<T, U: PartialEq<T>>(old: U, new: T) -> T {
    let _ = old == new;
    new
}

TL; DR due to impl<T> PartialEq<Option<T>> for Option<T> where T: PartialEq<T>, old.as_ref() and neew.data.as_ref() should return the same type Option<&i32> which extends the lifetime of old (&mut Option<i32> actually) by using sub the next line and causes lifetime overlapping in *old (another &mut Option<i32> actually).

1 Like