Lifetime in future confusion

code:

use std::{
    future::Future,
    pin::Pin,
    task::{Context, Poll},
};

struct MyFut<'a> {
    data: Box<String>,
    foo: Foo<'a>,
}

struct Foo<'a>(&'a mut str);

impl Future for MyFut<'_> {
    type Output = ();

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let this = self.get_mut();
        this.foo = Foo(&mut this.data);
        Poll::Ready(())
    }
}

fn main() {}

error:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
  --> src/main.rs:18:25
   |
18 |         let this = self.get_mut();
   |                         ^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime defined on the method body at 17:23...
  --> src/main.rs:17:23
   |
17 |     fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
   |                       ^^^^^^^^^
note: ...so that the types are compatible
  --> src/main.rs:18:25
   |
18 |         let this = self.get_mut();
   |                         ^^^^^^^
   = note: expected `Pin<&mut MyFut<'_>>`
              found `Pin<&mut MyFut<'_>>`
note: but, the lifetime must be valid for the lifetime `'_` as defined on the impl at 14:23...
  --> src/main.rs:14:23
   |
14 | impl Future for MyFut<'_> {
   |                       ^^
note: ...so that the expression is assignable
  --> src/main.rs:19:20
   |
19 |         this.foo = Foo(&mut this.data);
   |                    ^^^^^^^^^^^^^^^^^^^
   = note: expected `Foo<'_>`
              found `Foo<'_>`

How can I convert this.data to 'a lifetime?

You're trying to create a self-referential struct. It's not safe in Rust. You can't have a struct holding a temporary borrow of its own field. Structs can borrow only data stored elsewhere, already outside of the struct.

When you use async fn or async {} blocks, then you can have a Future that de-facto references its own data, but that is an exception for the async sugar, and not available otherwise. struct T<'a> is for views into data elsewhere, not for storing things by reference.

If you really need two fields having access to the same data, then you must use Arc instead of temporary borrows.

2 Likes

Thank you for your answer.
I still have some doubts about this problem.
MyFut.data is boxed, my understanding is that it can be safely moved, even if it is referenced by MyFut.foo.
If possible, Can I use unsafe code to solve this problem?
Thinks.

It's still not safe even with a Box. Technically Box is even redundant, because String contains heap data by itself (it's a wrapper around Box<str>).

The problem is that the borrow checker has no way of knowing that assignment to one field breaks another. If something did myfut.data = Box::new(String::new()), then this would drop the previous box in data, free the previous string, and leave myfut.foo with a dangling pointer to freed memory.

The second hard problem is with &mut being always completely exclusive, so you can't have two fields that have access to the same String at the same time. This is usually solved by borrowing making its source temporarily unmovable and inaccessible, but putting them both in a movable struct makes it conceptually impossible.

There are a few crates like rental, owning_ref, ouroboros, and self_cell that try to work around it and expose a safe API. They may have limitations and be awkward to use.

If you know what you're doing and are certain that data won't change while foo exists, then you can use unchecked *mut pointer.

2 Likes

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.