How to fix this lifetime issue?

I have such code snippet:

#[test]
fn test_lifetime() {
    struct Aa<'a> {
        _1: PhantomData<&'a ()>,
    }

    fn f<'a>(_b: &'a mut Aa<'a>) {}

    let mut v = Aa {
        _1: PhantomData::default(),
    };
    f(&mut v);

    let c = &v;
}

IMO, the mutable reference seems should be dropped as soon as function f is finished, but it returns an error as:

error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
  --> src/lifetime.rs:16:13
   |
14 |     f(&mut v);
   |       ------ mutable borrow occurs here
15 |
16 |     let c = &v;
   |             ^^
   |             |
   |             immutable borrow occurs here
   |             mutable borrow later used here

For more information about this error, try `rustc --explain E0502`.
warning: `play` (bin "play" test) generated 7 warnings
error: could not compile `play` due to previous error; 7 warnings emitted

I can not really understand why &v will be used as a mutable borrow either.

Have I missed some part of the lifetime rules?

P.S. compiler version: rustc 1.64.0-nightly (38b72154d 2022-07-11)

This is a bad anti-pattern in Rust. A mutable reference &'a mut … to a type that has the same lifetime 'a as a parameter will always have the effect that the value is borrowed for its entire existence, and thus you can do nothing at all with the value v after calling f(&mut v) (besides implicitly dropping it).

A shared reference &'a … to a type that has the same lifetime 'a as a parameter (i.e. something like &'a A<'a>) will sometimes be problematic, too, if that type (Aa<'a>) is invariant in the lifetime 'a.

4 Likes

The reason why the error message reads like that is because the &v constitutes a usage of v, and any usage of v requires the lifetime 'a in its type Aa<'a> to be still “alive”.

Since the previous call f(&mut v) ensured that the mutable borrow of v happens (at least) as long as the lifetime 'a (because of the type &'a mut Aa<'a> which says that the mutable borrow lasts for (at least) the lifetime 'a), the compiler will consequently consider the usage of v in &v to keep the mutable borrow alive, and report it, perhaps somewhat unintuitively, as “mutable borrow […] used here”.

So the lifetime of &'a mut is actually reduced to the lifetime of borrow inside Aa itself, thus I can never release the mutable borrow until it's dropped. Is that correct?

In fact, there should be two different lifetime annotations here, but I was thinking about reusing one of them. It seems I have to introduce a new lifetime to fix this issue.

Yes, sound correct, and the solution is good, too.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.