Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Why can't a struct impl await its task and get result when it can await its methods?

In some cases it would be useful for a struct/object to be able to start some tasks and then allow struct users to stop these tasks and await for result. I could of course send result with channel or shared resource but using return value would be cleaner in many cases. Also I understand from experience in other async languages that it is good practice to always await your tasks at some point in code.

I cannot make the borrow checker to accept the following attempt to implement the idea over. Can someone explain it? and even better can I find a way out of it?


use tokio::time;
use tokio::task::JoinError;

struct S {
    pub task: tokio::task::JoinHandle<bool>,
}

impl S {
    pub async fn new() -> Self {
        let task = tokio::task::spawn(async {
            time::sleep(time::Duration::from_secs(10)).await;
            true
        });
        Self { task }
    }

    pub async fn join_and_get_result(&self) -> Result<bool, JoinError> {
        self.task.await // cannot move out of `self.task` which is behind a shared reference
    }
}

#[tokio::main]
async fn main() {
    let s = S::new().await;
    s.join_and_get_result().await;
    let result = s.task.await; // this is fine for borrow checker but requires to expose internal implementation
}

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

Calling .await on a task consumes the task. You can fix the error by making join_and_get_result take self and not &self, which will move S into the function making it possible to call .await on the task:

use tokio::task::JoinError;
use tokio::time;

struct S {
    pub task: tokio::task::JoinHandle<bool>,
}

impl S {
    pub async fn new() -> Self {
        let task = tokio::task::spawn(async {
            time::sleep(time::Duration::from_secs(10)).await;
            true
        });
        Self { task }
    }

    pub async fn join_and_get_result(self) -> Result<bool, JoinError> {
        self.task.await
    }
}

#[tokio::main]
async fn main() {
    let s = S::new().await;
    let result = s.join_and_get_result();
}

Playground

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading