关于发电机我有个问题:
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
的闭包中,根本不应该有借用。
2条答案
按热度按时间omqzjyyz1#
Generators是一个不稳定的特性,目前只在nightly中可用,它可以与其他语言(例如Javascript、Go、Python)中的生成器或协程相比较。
生成器本质上是状态机,可以使用
yield
暂停执行,稍后再次恢复,并有可能在每个转换中传递数据。这种模式非常适合异步编程,Rust编译器扩展某些async
代码以使用生成器,即使在没有启用夜间特性的情况下您自己无法显式使用它们。这些消息可能是一个bug,没有正确地进行功能门控,或者对于
async
去糖生成的代码和您自己明确编写的代码,显示不同的错误可能太复杂了。因此,让我们忽略生成器,它对于您的实际问题来说有点像转移注意力。
您正在将
x
移动到闭包中:然后闭包将
x
再次移到async
块中,问题是run
的签名接受Fn
闭包--一个不能修改其环境但 * 可以 * 被多次调用的闭包,然而您提供的闭包在第一次被调用时将x
移到async
块中,因此第二次调用它将是无效。假设你已经说过你不能改变
run
来接受FnOnce
,你需要通过防止f
移动x
来使f
可被调用多次。Arc
的整个设计都是关于廉价克隆的。它的廉价是因为它只复制指向你的数据的指针,并增加引用计数,这就是它如何知道什么时候释放保存数据的内存是安全的。如果你从来没有克隆过Arc
,那么你几乎可以肯定一开始就不需要它!wz8daaqr2#
“move occurs due to use in generator”错误似乎在这里有点混淆,但总体问题与生成器无关。
您的
run
函数接受Fn()
函数,而不接受FnOnce()
函数。这意味着传递给run
的函数必须能够执行多次。鉴于此,您的代码无法按编写的方式工作,因为在将x
分配给y
时,运行您的函数会占用x
的所有权。如果对该函数的第一次调用占用x
的所有权,函数第二次运行时应该发生什么?x
的所有权在第一次执行时就已经交给了future,两个对象不能拥有相同的值。既然你有一个
Arc
,我猜你已经有了这种感觉,但是如果没有更多的逻辑,它是不会发生的。既然你使用的是Arc
,你需要在函数的开始克隆x
,这样每次函数运行时,它都有自己的Arc对象来处理和操作。