rust - Rust 中的“由于在生成器中使用而发生移动”错误是什么意思?
问题描述
我对发电机有这个问题:
use tokio::runtime::Runtime;
use tokio::task::JoinHandle;
use std::sync::Arc;
pub fn run(f: Box<dyn Fn() -> Result<(), ()> + Send>) {
f();
}
fn main() {
let x = Arc::new(0);
run(Box::new(move ||{
let rt = Runtime::new().unwrap();
let _t = rt.block_on(async move {
let y = x;
});
Ok(())
}));
}
错误:
error[E0507]: cannot move out of `x`, a captured variable in an `Fn` closure
--> src/main.rs:13:41
|
10 | let x = Arc::new(0);
| - captured outer variable
...
13 | let _t = rt.block_on(async move {
| _________________________________________^
14 | | let y = x;
| | -
| | |
| | move occurs because `x` has type `Arc<i32>`, which does not implement the `Copy` trait
| | move occurs due to use in generator
15 | | });
| |_________^ move out of `x` occurs here
我不明白为什么x
要借用,如果在闭包和块中,我都使用move
. 所以x
应该移到rt.block_on
's 关闭。根本不应该借钱。
解决方案
生成器是一个不稳定的特性,目前仅在 nightly 中可用,可以与其他语言(例如Javascript、Go、Python)的生成器或协程进行比较。
生成器本质上是状态机,可以使用暂停执行yield
,然后再次恢复,并有可能在每次转换中传递数据。这种模式非常适合异步编程,Rust 编译器扩展了某些async
代码以使用生成器,即使你不能在没有启用 nightly 功能的情况下自己显式使用它们。
async
这些消息可能是一个错误,即这些消息没有正确地进行功能控制,或者对于脱糖生成的代码与您自己明确编写的代码相比,呈现不同的错误可能太复杂了。
因此,让我们忽略生成器,这对于您的实际问题来说有点红鲱鱼。
您正在x
关闭:
let x = Arc::new(0);
run(Box::new(move ||{
// 'move' closure uses x so x is moved
}));
然后闭包x
再次移动到一个async
块中。问题在于run
接受Fn
闭包的签名——一个不能修改其环境但可能被多次调用的闭包。但是,您提供的闭包在第一次调用时会移动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
那么您几乎可以肯定一开始就不需要它!
推荐阅读
- css - 如何在移动设备和桌面设备中以不同方式堆叠两个元素?
- r - R函数循环两次?
- python - 有没有办法使用 PyTables 创建一个指定的组(如果它不存在),或者打开一个现有的组(如果存在)?
- javascript - Gauge Taiko:给定选择器列表/计数遇到的项目
- groovy - Gremlin 查询如何在我的遍历的同一分支中比较边缘的属性?
- database - 为什么将 TEXT 列从长日志文本更新为空字符串后,postgres 表大小会增加?
- angularjs - AngularJS将范围传递到具有相同控制器的不同页面
- prometheus - Prometheus:带有 AWS SNS 的 AlertManager
- sql-server - 如何将存储过程的输出映射到 SQL 任务中的变量?
- javascript - “this.props.dispatch”调度错误动作