Rust AssertUnwindSafe如何与CatchUnwind未来一起使用

Rust AssertUnwindSafe如何与CatchUnwind未来一起使用,rust,async-await,future,Rust,Async Await,Future,我希望能够向函数传递一个可变引用,但捕获可能来自该函数的展开。其目的是用于编写一些测试包装(设置、拆卸),而不是一般的错误处理 如果我使用的是典型的同步代码,我可以让它编译和工作 struct MyStruct{ n:u32 } fn my_func(s:&mut MyStruct){ s、 n+=1; 惊慌失措!(“哦,不!”); } fn main(){ 设mut-ctx=MyStruct{n:1}; 让mut wrapper=std::panic::AssertUnwindSafe(&mu

我希望能够向函数传递一个可变引用,但捕获可能来自该函数的展开。其目的是用于编写一些测试包装(设置、拆卸),而不是一般的错误处理

如果我使用的是典型的同步代码,我可以让它编译和工作

struct MyStruct{
n:u32
}
fn my_func(s:&mut MyStruct){
s、 n+=1;
惊慌失措!(“哦,不!”);
}
fn main(){
设mut-ctx=MyStruct{n:1};
让mut wrapper=std::panic::AssertUnwindSafe(&mut-ctx);
让结果=标准::恐慌::抓住你放松(移动| |{
my_func(*包装器);
});
//在这里清理一下'ctx'。
如果让错误(Err)=结果{
标准::恐慌::恢复/放松(错误);
}
}
然而,我还没有弄清楚如何使用futures和async/await来实现这一点。在这种情况下,我将尝试调用已声明为async的函数。我尝试过以下代码:

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);
}
但是,我通常会出现以下错误:

类型
&mut MyStruct
可能无法安全地跨展开边界传输


我相信,
AssertUnwindSafe
应该帮助解决这些问题,就像他们处理同步代码一样。但是在AssertUnwindSafe和async/await的交叉点上,显然有一些我不理解的地方。

对于
std::panic::catch_unwind
,提供的闭包必须是
UnwindSafe
,在内部使用可变引用将使闭包无法实现
UnwindSafe
。这就是为什么包装引用并移动引用会起作用

但是,使用
futures::future::FutureExt::catch_unwind
,提供的未来必须是
UnwindSafe
,并且
run\u async
生成的未来不在乎引用是否来自
AssertUnwindSafe
包装器,因为您在调用它之前正在对它进行解压缩。因此,你应该断言未来本身是安全的:

use futures::future::FutureExt;
结构MyStruct{
n:i32
}
异步fn运行\u异步(s:&mut MyStruct){
s、 n+=1;
惊慌失措!(“哦,不!”);
}
#[tokio::main]
异步fn main(){
设mut-ctx=MyStruct{n:1};
让结果=异步移动{
//AssertUnwindSafe走向未来
std::panic::AssertUnwindSafe(运行异步(&mut ctx)).catch\u unwind().wait
}.等待;
println!(“{:?}”,结果);
}

谢谢!虽然我没有在问题中完全指定,但在等待异步块之后,我还想使用
ctx
。因此,我需要像
let wrapper=&mut ctx
在异步块之前,然后
运行\u async(wrapper)
以确保
ctx
不会移动到异步块中,而是移动到异步块的可变引用中。然而,我缺少的是理解
AssertUnwindSafe
应该包装未来,而不是未来的价值。