发电机 https://doc.rust-lang.org/stable/unstable-book/language-features/generators.html是一个不稳定的功能,目前仅在 nightly 中可用,可以与其他语言中的生成器或协程进行比较(例如JavaScript https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators#generator_functions, Go https://golangbot.com/goroutines/, Python https://wiki.python.org/moin/Generators).
生成器本质上是状态机,可以使用以下命令暂停执行yield
然后再次恢复,并且可以在每次转换中传递数据。这种模式非常适合异步编程,并且 Rust 编译器扩展了某些async
代码来使用生成器,即使在没有启用夜间功能的情况下您自己也无法显式地使用它们。
这些消息可能没有正确地进行功能门控,这可能是一个错误,或者它可能太复杂,无法为生成的代码呈现不同的错误async
脱糖比您自己明确编写的代码要好。
因此,让我们忽略生成器,这对您的实际问题来说有点转移注意力。
你正在移动x
进入闭包:
let x = Arc::new(0);
run(Box::new(move ||{
// 'move' closure uses x so x is moved
}));
然后关闭动作x
再次进入一个async
堵塞。问题是签名run
接受一个Fn
闭包 - 不能修改其环境的闭包,但may被多次调用。然而,你提供的关闭移动x
进入async
第一次调用会阻塞,第二次调用就会无效。
既然你说过你无法改变run
接受一个FnOnce
,你需要做f
通过阻止其移动来多次调用x
。您可以通过克隆它来做到这一点:
fn main() {
let x = Arc::new(0);
run(Box::new(move || {
let rt = Runtime::new().unwrap();
// each time the closure is called, it will pass a clone of the Arc
// into the task, so it can be called more than once
let x = x.clone();
let _t = rt.block_on(async move {
let y = x;
});
Ok(())
}));
}
整个设计的Arc
是关于廉价克隆。它很便宜,因为它只复制指向数据的指针并增加引用计数,这就是它知道何时可以安全释放保存数据的内存的方式。如果您从未克隆过Arc
那么你几乎肯定一开始就不需要它!