"lifetime may not live long enough" extracting reference from struct field

I have a struct that contains a reference that lives longer that that struct. I am trying to simply extract that reference, but it keeps getting reborrowed to only live as long as the struct.

minimal example:

struct MutWrap<'a>(&'a mut u8);

impl<'a> MutWrap<'a> {
    fn inner(&self) -> &'a mut u8 {
        self.0
    }
}

You can't get a &'long mut _ from a &'short mut &'long mut _ unless you have a way to replace the inner &'long mut _.

You can't get a &mut _ from a & ... &mut _ unless there's some interior mutability inbetween.

1 Like

reading through this previous thread tells be this is because mutable references are not Copy and thus need to be reborrowed, but why can't I reborrow while maintaining the longer lifetime?

I believe you have to consume self anyway, to prevent two active mut refs.

            fn inner(self) -> &'a mut u8 {

In case it isn't clear, this is what you can do:

impl<'a> MutWrap<'a> {
    fn into_inner(self) -> &'a mut u8 {
        self.0
    }
}

self is taken by value — consumed — and therefore the reference can be moved out of it.

If your original program compiled, then you would be able to produce a duplicate of the reference (which is UB) by calling inner() twice.

For example, if your API could compile...

fn example(mw: MutWrap<'_>) {
    let one = mw.inner();
    let two = mw.inner();
    
    // Aliased `&mut u8` -- UB
    *one = 1;
    *two = 2;
    *one = 1;
    *two = 2;
    *one = 1;
    *two = 2;
}
1 Like

but why can't the compiler just... not let you do that?

I mean, it does -- by making your OP an error.


The meaning of the below API is "if you give me an arbitrary short shared borrow of self, I can give you a &'a mut u8 (and then release the arbitrary short shared borrow of self)".

fn inner(&self) -> &'a mut u8 {

There is no sound way to achieve that.[1]

If you want the call site to be restricted more, so to as allow the method to do something else, you need a different API. Taking self by value has already been suggested. Reborrowing is another possibility:

// N.b. not `&'a mut u8` or you could end up with two at the same
// time again if it were allowed, because again the outer lifetime
// could expire while `'a` is still alive
fn inner(&mut self) -> &mut u8 {

The only way to soundly get a &'a mut u8 to the field without taking ownership would be

fn inner(&'a mut self) -> &'a mut u8 {

but while this works from a type perspective, it's useless from a practical perspective -- you might as well take it by value.


  1. and have the returned &mut u8 be the field, versus something you just allocated and leaked or such ↩︎

3 Likes

I want the semantics of that without releasing the borrow... is there really no way to achieve that?

I suppose what I want is &move &'a mut u8, which doesn't exist yet..

I want the semantics of that without releasing the borrow... is there really no way to achieve that?

For soundness, such a borrow has to last as long as 'a, and at that point you might as well take the MutWrap by value with an into_inner().

Another possibility would be Cell<Option<&'a mut u8>>, which lets you extract at most one &'a mut u8 from a borrow of it. That’s run-time checking, but sometimes run-time checking is the cleaner way to go.

I suppose what I want is &move &'a mut u8 , which doesn't exist yet..

What's the advantage over passing the &'a mut u8 itself, or a wrapper struct, by value?

Or rather: why don't you want to pass MutWrap by value?

I'm pretty sure this is actually an issue of cross-function partial borrows?

In my actual code, the function that tries to return the long reborrow actually does some manipulations to the field before returning access to it, and I want to encapsulate my code so that you can't read the field without first doing those manipulations.

In my case I was able to just return a new mutable reference to the underlying data once the wrapper struct gets destroyed.

If you want to give an exclusive reference that guarantees validity for the entire duration of 'a, then this works:

struct MutWrap<'a>(&'a mut u8);

impl<'a> MutWrap<'a> {
    fn inner(&'a mut self) -> &'a mut u8 {
        self.0
    }
}

although it's pretty useless, because you can call such method only once anyway (since it promises exclusive access for the entire lifetime, there's only one whole lifetime and one exclusive access to give). Usually fn borrow<'short>(&'short mut self) -> &'short mut u8 is used (which is the default, and the 'short label can be omitted).

Because the syntax you want is giving a promise that it cannot keep.

You can't have shared &self that can be called many times, and each time guarantee that it's an exclusive reference that guarantees that nothing else can have it at the same time, while also allowing everything else get it anytime it wants.

Your syntax also means &'short self -> &'long u8, which means that you "lock" self only for a short time (which would allow it to be called again after the short time), but also promise that what it has lent remains locked exclusively and valid for a long time (so allowing more "exclusive" duplicate references to be given out again before the long time ends).

That code is just a paradox.

Note that lifetimes of loans are analyzed at compile time, so they must follow rules of lifetime annotations and general rules of the borrow checker, and the compiler can't be too smart about how they are used in practice.

If you need to flexibly give out many references, and mutate the data at the same time, you need interior mutability. You can have:

fn inner(&self) -> &'a RefCell<T>  // or Rc<RefCell<T>>

And this allows the program to track actual usage of the references and exclusivity at run time.

1 Like

ok yeah that makes sense, but if i change the reciver to &mut self i get the same error.

That's because &'short mut self (the default when you write &mut self) effectively acts in the same way as &self. The unrelated short lifetime is not restricted by anything, so it can have a zero duration, and therefore allow calling &mut self as many times as you want, and again each call would be returning a copy of &'long mut, creating a lot of "exclusive" duplicates.

It's correct only when you have duration of &mut self loan match duration of the returned &mut u8 reference. You can borrow self for a short time to get a short-lived reference, or borrow self for a long time to get a long-lived reference. But self has to stay borrowed for at least that long, because that "locks" access to it, and prevents it from giving out more exclusive references in the meantime.