Confused about if let borrow lifetime

struct Foo {
    num: Option<i32>,
}

struct Test {
    foo: Foo
}

impl std::ops::Deref for Test {
    type Target = Foo;
    fn deref(&self) -> &Self::Target {
        &self.foo
    }
}
impl std::ops::DerefMut for Test {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.foo
    }
}

trait Bar {
    fn bar(&mut self) -> &i32;
}
impl Bar for Test {
    fn bar(&mut self) -> &i32 {
        if let Some(ref n) = self.num {
            return n;
        }
        self.num = Some(123);
        self.num.as_ref().unwrap()
    }
}

fn main() {
    let mut test = Test{foo:Foo{num: None}};
    println!("{}",test.bar());
}

compile error:

error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
  --> src/main.rs:29:9
   |
25 |     fn bar(&mut self) -> &i32 {
   |            - let's call the lifetime of this reference `'1`
26 |         if let Some(ref n) = self.num {
   |                              ---- immutable borrow occurs here
27 |             return n;
   |                    - returning this value requires that `*self` is borrowed for `'1`
28 |         }
29 |         self.num = Some(123);
   |         ^^^^ mutable borrow occurs here

the following code is fine:

struct Foo {
    num: Option<i32>,
}

trait Bar {
    fn bar(&mut self) -> &i32;
}
impl Bar for Foo {
    fn bar(&mut self) -> &i32 {
        if let Some(ref n) = self.num {
            return n;
        }
        self.num = Some(123);
        self.num.as_ref().unwrap()
    }
}

fn main() {
    let mut test = Foo{num: None};
    println!("{}",test.bar());
}

I dont't understand why?Can someone answer it for me?

Hi, because about 8 hours passed and there is no answer, I try to give one.

I think/guess it because that Rust is unable to reason across a function boundary. Test implement Deref/DerefMut, then self.num is accessed via the fn deref(). I guess Rust just simply assume that the entire self is borrowed when fn deref() is called, then any modification (that occur during which the borrow live) on self is refused.

Here is a modified version of your second code example that Rust refuse to compile. I think the reason is the same. But the compiler wrongly say that:

 cannot assign to `test.other` because it is borrowed

but actually test.num is borrowed. I think the error message should be improved in these cases.

The issue is indeed the Deref. You can do this instead:

impl Bar for Test {
    fn bar(&mut self) -> &i32 {
        let num = &mut self.num;
        if let Some(ref n) = num {
            return n;
        }
        *num = Some(123);
        num.as_ref().unwrap()
    }
}
1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.