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.