Unable to use &self to call a method of a struct inside another method of the struct

0

trying to use &self to call an async method of a struct within another async method. The compiler complains like so:

11 |     async fn mult(&mut self) {
   |                   ---------
   |                   |
   |                   `self` is a reference that is only valid in the associated function body
   |                   let's call the lifetime of this reference `'1`
12 |         self.a *= 2;
13 |         tokio::spawn(self.disp());
   |                      ^^^^^^^^^^^
   |                      |
   |                      `self` escapes the associated function body here
   |                      argument requires that `'1` must outlive `'static`

While I understand that the compiler cannot guarantee that &self would outlive the execution of display, how do I even tackle this ? Calling the method as an associated function comes to mind, but that is most likely practical only in toy code snippets like this.

Code :

use tokio;
struct A {
    a: i32,
}

impl A {
    async fn disp(&self) {
        println!("{}", self.a);
    }

    async fn mult(&mut self) {
        self.a *= 2;
        tokio::spawn(self.disp());
    }
}

#[tokio::main]
async fn main() {
    let c = A{a: 2};

    c.mult();
}

Generally the answer is that everything the async task (future you are spawning) uses has to be not a borrow, or otherwise ensured to live as long as the task needs.

In this case, you can simply have disp copy the data it needs instead of borrowing it from self:

struct A {
    a: i32,
}

impl A {
    fn disp(&self) -> impl std::future::Future<Output = ()> {
        let a = self.a;
        async move {
            println!("{a}");
        }
    }

    async fn mult(&mut self) {
        self.a *= 2;
        tokio::spawn(self.disp());
    }
}

#[tokio::main]
async fn main() {
    let mut c = A { a: 2 };

    c.mult().await;
}

Note that disp is no longer an async fn. This doesn't change the way it is used, but gives more control over what the returned future captures, so we can arrange that it doesn't capture self.

If you wanted the spawned task to be able to modify the state then you would need to start looking into means of shared mutability (e.g. Arc + Mutex) or structuring your code differently.


By the way, use tokio; is not necessary; it does almost nothing. In general, use a::b; brings b into the current module, but a has to be something that's already in scope, so a single-component use does nothing and is not necessary. (There are some exceptions, but they mostly only matter if you are writing a pub use.)

Thanks for the quick and detailed response firstly! I understand and I think I might just have to think about restructuring my code.

And thank you for that little bit about the useless use.

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.