Catch panic in async call

Hi,

I would like to catch the possible panic when calling a async function.
I read this documentation : JoinHandle in tokio::task - Rust
and I intend to use this kind of code :

let panics = tokio::spawn(async move {
                        test.run().await
                    });
                    
match panics {
// do something according if it is ok or err
...

The run function looks like this :

pub async fn run(&self) -> Result<()> {
        ...
}

With result like :

pub type Result<T> = core::result::Result<T, Error>;
pub type Error = Box<dyn std::error::Error>;

Actually I got this error :

let panics = tokio::spawn(async move {
    |  __________________________________------------_^
    | |                                  |
    | |                                  required by a bound introduced by this call
91  | |                         test.run().await
92  | |                     });
    | |_____________________^ `dyn StdError` cannot be sent between threads safely
    |
    = help: the trait `Send` is not implemented for `dyn StdError`
    = note: required for `Unique<dyn StdError>` to implement `Send`

I understant that Send must be add somewhere, but I don't know if it is in error type or else ?
If I add like this, I have a lot more error everywhere... :

pub type Error = Box<dyn std::error::Error + Send>;

personally, I use this

pub type Error = Box<dyn core::error::Error + Send + Sync + 'static>;

Wouldn't trivial way simply work:

    if let Ok(panics) = panics.await {
         …
    }

Why do you use unwrap?

1 Like

Oh sorry, I just find that at the moment your answer I was deleting my post.

I have a last problem :

 pub async fn handle(&self) -> Result<()> {  
...
      let test = Test::new();
      let panics = tokio::spawn(async move { test.run().await });
...

handle is a function that I call on a struct.
I have move word because I think that I need to pass the test value. But I do not need to pass &self.
and I have this error :

 pub async fn handle(&self) -> Result<()> {
   |                      -----
   |                      |
   |                      `self` is a reference that is only valid in the method body
   |                      let's call the lifetime of this reference `'1`
...
91 |                     let panics = tokio::spawn(async move { test.run().await });
   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |                                  |
   |                                  `self` escapes the method body here
   |                                  argument requires that `'1` must outlive `'static`

In fact with move or not, it is the same...

Try sharing more complete code. The connection between Test is not clear from the code you shared.

this compile:

struct Test {}

impl Test {
    pub async fn run(&self) -> Result<()> {
        panic!("panic");
    }
}

struct Handle {}

impl Handle {
    pub async fn handle(&self) -> Result<()> {
        let test = Test {};

        let panics = tokio::spawn(async move { test.run().await });

        match panics.await {
            Ok(ok) => ok,
            Err(err) => Err(err.into()),
        }
    }
}

#[tokio::main]
async fn main() {
    let handle = Handle {};
    let result = handle.handle().await;
    println!("{:?}", result)
}

pub type Result<T> = core::result::Result<T, Error>;
pub type Error = Box<dyn core::error::Error + Send + Sync + 'static>;

This is my code that has error :

pub struct Test {
    id: i32,
    name: String,
    configuration: Result<TestFeature>, 
}

impl TestFeature {
    pub async fn run(&self) -> Result<()> {
        panic!("panic");
    }
}

pub struct TestFeature {
    request: Option<String>,
}

struct Handle {
    test_features: Vec<Test>,
}



impl Handle {
    pub async fn handle(&self) -> Result<()> {
        for t in &self.test_features {
            match &t.configuration {
                Err(e) => println!("Err"),
                Ok(test) => {
                    let panics = tokio::spawn(async move { test.run().await });

                    match panics.await {
                        Ok(_) => println!("OK"),
                        Err(_) => println!("KO"),
                    }
                }
            }
        }
        Ok(())
    }
}

#[tokio::main]
async fn main() {
    let mut handle = Handle {test_features: Vec::new()};
    handle.test_features.push(
       Test{
         id: 1,
         name: String::from("A test"),
         configuration: Ok(TestFeature {
                             request: Some(String::from("a request"))
                         })
        }
    );
    let result = handle.handle().await;
    println!("{:?}", result)
}

pub type Result<T> = core::result::Result<T, Error>;
pub type Error = Box<dyn core::error::Error + Send + Sync + 'static>;

test is a borrow from &self so you can't do that. So either you pass self by value (but then, it is owned by the handle() function) or you find another way.

pub struct Test {
    id: i32,
    name: String,
    configuration: Result<TestFeature>, 
}

impl TestFeature {
    pub async fn run(&self) -> Result<()> {
        panic!("panic");
    }
}

pub struct TestFeature {
    request: Option<String>,
}

struct Handle {
    test_features: Vec<Test>,
}



impl Handle {
    pub async fn handle(self) -> Result<()> {
        for t in self.test_features {
            match t.configuration {
                Err(e) => println!("Err"),
                Ok(test) => {
                    let panics = tokio::spawn(async move { test.run().await });

                    match panics.await {
                        Ok(_) => println!("OK"),
                        Err(_) => println!("KO"),
                    }
                }
            }
        }
        Ok(())
    }
}

#[tokio::main]
async fn main() {
    let mut handle = Handle {test_features: Vec::new()};
    handle.test_features.push(
       Test{
         id: 1,
         name: String::from("A test"),
         configuration: Ok(TestFeature {
                             request: Some(String::from("a request"))
                         })
        }
    );
    let result = handle.handle().await;
    println!("{:?}", result)
}

pub type Result<T> = core::result::Result<T, Error>;
pub type Error = Box<dyn core::error::Error + Send + Sync + 'static>;
``
1 Like

Thank you. I realise I do not understand this with the error message.
So finally, I make the possibility to clone a Test.
As when a test is configured, we only need its value to pass the test.