Problem with struct containing cell containing reference


#1

Hi Rust community!

I have trouble understanding why this code snipped does not compile:

use std::cell::Cell;

struct Foo<'a> {
    _a: Cell<&'a i32>,
    _b: &'a i32,
}

fn main() {
    let x = 0;
    let a = Cell::new(&x);
    let b = 0;
    let _c = Foo{ _a: a, _b: &b};
}

The compiler gives the following error:

error: `b` does not live long enough
  --> test.rs:15:1
   |
14 |     let _c = Foo{ _a: a, _b: &b};
   |                               - borrow occurs here
15 | }
   | ^ `b` dropped here while still borrowed
   |
   = note: values in a scope are dropped in the opposite order they are created

error: aborting due to previous error(s)

Why does the compiler think that b is still borrowed at line 15? Shouldn’t _c already have been dropped at that point (meaning that b is no longer borrowed)?
What confuses me even more is that the example compiles without errors if I replace Cell with any other Wrapper (like Box or Rc).

The snipped also compiles if I change the definition of Foo to

struct Foo<'a, 'b> {
    _a: Cell<&'a i32>,
    _b: &'b i32,
}

but I would like to understand why the first version does not work.


#2

Cell is invariant over the lifetime it’s provided, since it allows interior mutability. As a consequence, your Foo then becomes invariant over 'a as well. That, in turn, requires that _a and _b have the same lifetime, which they don’t.

Once you switch to Foo<'a,'b> and use them for the two references, _a and _b no longer need to have the same lifetime.

https://doc.rust-lang.org/nomicon/subtyping.html may help you understand this a bit better.


#3

It would also work for you to just declare b before a, since &i32 is variant – it can shorten to the lifetime of the cell reference.

Regardless of variance, you can give local variables the same lifetime by assigning them together:

    let (a, b) = (Cell::new(&x), 0);

or even just by declaring them together and then assigning them separately:

    let (a, b);
    a = Cell::new(&x);
    b = 0;

#4

Thanks for the replies.
I think I understand the problem now. I did not know that Cell is invariant (but it makes sense that is has to be).