I want to be able to pass an mutable reference to function, but catch unwinds that may come from that function. The purpose is for use in writing some test wrappers (setup, teardown), not general error handling.
If I were using typical sync code, I can get this to compile and work...
struct MyStruct {
n: u32
}
fn my_func(s: &mut MyStruct) {
s.n += 1;
panic!("Oh no!");
}
fn main() {
let mut ctx = MyStruct { n: 1 };
let mut wrapper = std::panic::AssertUnwindSafe(&mut ctx);
let result = std::panic::catch_unwind(move || {
my_func(*wrapper);
});
// Do some cleanup of `ctx` here.
if let Err(err) = result {
std::panic::resume_unwind(err);
}
}
However, I haven't been able to figure out how to do this using futures and async/await. In that case, I would be trying to call a function that has been declared as async. I have tried various things like the code below:
async fn run_async(s: &mut MyStruct) {
s.n += 1;
panic!("Oh no!");
}
#[tokio::main]
async fn main() {
let mut ctx = MyStruct { n : 1 };
let wrapper = std::panic::AssertUnwindSafe(&mut ctx);
let result = async move {
run_async(*wrapper).catch_unwind().await
}.await;
println!("{:?}", result);
}
However, I typically will end up with an error such as:
the type
&mut MyStruct
may not be safely transferred across an unwind boundary`.
I was under the belief that AssertUnwindSafe
was supposed to help with these problems, as they did with the sync code. But there is obviously something I'm not understanding at the intersection of AssertUnwindSafe and the async/await.
ctx
after awaiting the async block. Thus, I needed a line likelet wrapper = &mut ctx;
before the async block, thenrun_async(wrapper)
to ensure thatctx
isn't moved into the async block, but a mutable reference of it was. However, understanding thatAssertUnwindSafe
should be wrapping the future, not the value going into the future, was what I was missing. – Coincident