Lifetimes of structs with mutable references

This code does not compile due to conflicting requirements for a lifetime parameter.

struct A<'a> {
    i: &'a mut i32,
}

struct B<'b> {
    a: &'b mut A<'b>,
}

let mut j = 42;

let mut a = A {
    i: &mut j,   
};

fn f(a: &mut A) {
    let mut b = B {
        a: &mut a,
    };

    g(&mut b);
}

fn g(b: &mut B) {
    println!("{}", *b.a.i);
}

This is the resulting error message.

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'b` due to conflicting requirements
  --> src/main.rs:67:21
   |
67 |         let mut b = B {
   |                     ^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 66:20...
  --> src/main.rs:66:21
   |
66 |       fn f(a: &mut A) {
   |  _____________________^ starting here...
67 | |         let mut b = B {
68 | |             a: &mut a,
69 | |         };
70 | |
71 | |         g(&mut b);
72 | |     }
   | |_____^ ...ending here
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:68:16
   |
68 |             a: &mut a,
   |                ^^^^^^
note: but, the lifetime must be valid for the anonymous lifetime #2 defined on the body at 66:20...
  --> src/main.rs:66:21
   |
66 |       fn f(a: &mut A) {
   |  _____________________^ starting here...
67 | |         let mut b = B {
68 | |             a: &mut a,
69 | |         };
70 | |
71 | |         g(&mut b);
72 | |     }
   | |_____^ ...ending here
note: ...so that expression is assignable (expected &mut main::A<'_>, found &mut main::A<'_>)
  --> src/main.rs:68:16
   |
68 |             a: &mut a,
   |                ^^^^^^

Can someone explain what the probem is? The error message does not make sense to me.

Hi!

Your struct B is defined to contain a mutable reference to A, which contains a mutable reference to i32. You declare that both mutable references shall always have the same lifetime, called 'b.

This is easily fulfilled for shared references, because a long-lived reference can be coerced to a shorter lifetime. For mutable references, this is not possible, and generic lifetimes must always match exactly. (There was a really good argument for that, but I forgot.)

The error you got can be solved by explicitly using two lifetimes in the definition of B, and telling Rust that the outer lifetime must be longer than the inner:

/// Read: "Lifetime 'a must outlive lifetime 'b."
struct B<'b, 'a: 'b> {
    a: &b mut A<'a>, // two different lifetimes.
}

With this change, your code compiles and runs. (Playground link)

2 Likes

A longer lived lifetime cannot be substituted for a shorter one with mutable references because otherwise it would allow setting the long lived reference to point to a shorter lived one, leaving a dangling reference behind. This is the reason mutable references require exact lifetimes, and no subtyping/variance (ie allowing longer lived lifetime to be used in place of a shorter one).

2 Likes