Rust 闭包是'FnOnce',因为它将变量''.'移出其环境
我在将引用计数变量移动到闭包中时遇到了一些问题,这些闭包需要实现Rust 闭包是'FnOnce',因为它将变量''.'移出其环境,rust,Rust,我在将引用计数变量移动到闭包中时遇到了一些问题,这些闭包需要实现FnMut 以下代码可以正常工作: use std::rc::Rc; fn main() { let callback; let data: Rc<Vec<f32>> = Rc::new(Vec::new()); { let data = data.clone(); callback = move || { data.it
FnMut
以下代码可以正常工作:
use std::rc::Rc;
fn main() {
let callback;
let data: Rc<Vec<f32>> = Rc::new(Vec::new());
{
let data = data.clone();
callback = move || {
data.iter().for_each(|v| println!("{}", v));
};
consumer(callback);
}
}
fn consumer<D>(callback: D)
where
D: FnMut(),
{
}
let data: Rc<Vec<f32>> = Rc::new(Vec::new());
{
let data = data.clone();
callback = move || {
data.iter().for_each(|v| println!("{}", v));
};
编译器将生成以下错误:
-->src/test1.rs:11:20
|
11 |回调=移动|{
|^^^^^^此闭包实现的是'FnOnce',而不是'FnMut'`
12 |数据;
|----闭包是'FnOnce',因为它将变量'data'移出其环境
...
15 |消费者(回拨);
|-----实现`FnMut`的要求源于此
此示例适用于:
callback = move || {
let x = data[0];
println!("{}", x);
};
此示例不包括:
callback = move || {
let x = data;
println!("{}", x[0]);
};
callback = move || {
let x = data;
};
使用let data:Arc=Arc::new(Mutex::new(Vec::new());
,这可以:
callback = move || {
let x = data.lock().unwrap();
};
这并不是:
callback = move || {
let x = data;
println!("{}", x[0]);
};
callback = move || {
let x = data;
};
这背后的原因是什么
以下代码可以正常工作:
use std::rc::Rc;
fn main() {
let callback;
let data: Rc<Vec<f32>> = Rc::new(Vec::new());
{
let data = data.clone();
callback = move || {
data.iter().for_each(|v| println!("{}", v));
};
consumer(callback);
}
}
fn consumer<D>(callback: D)
where
D: FnMut(),
{
}
let data: Rc<Vec<f32>> = Rc::new(Vec::new());
{
let data = data.clone();
callback = move || {
data.iter().for_each(|v| println!("{}", v));
};
在这里,当调用闭包时,数据
被移动(由值使用,而不是引用)。在第一种情况下,它被立即删除;在第二种情况下,它被移动到局部变量x
中
如果从闭包中移出一个值,那么闭包只实现一次FnOnce
,并且只能调用一次,因为如果第二次调用闭包,它将不再使用该值
一般来说,任何简单提及变量名的行为都是一种移动。其他所有行为都是一个特殊的例外:
- 如果该值实现了
特征,那么它可以是一个副本而不是移动,这意味着原始文件保持有效。(此处不适用,因为Copy
不实现Rc
)Copy
- 如果调用方法,如
或.iter()
,则该方法可以采用.lock()
或&self
;Rust会自动采用引用,就像编写了&mut self
一样(&data).iter()
是一个宏,为了方便起见,它隐式引用其参数println!
如果您编写
&data;
或让x=&data;
代替,那么您将创建一个引用,该引用不会移动原始值。为了从与公认答案稍有不同的角度涵盖同一主题,有两个概念是相关的,但不同:
移动
确保将数据
值移动到闭包中,并且不会超过闭包
move | | data.lock().unwrap()
,则闭包的调用实现需要&self
,并且可以多次调用,只有在删除闭包时才会销毁数据
在move | | data
的情况下,闭包的调用实现采用self
并立即消耗数据(以及闭包的其余部分),这就是为什么它只能调用一次,并在返回时丢弃数据
在#2中,使用数据的闭包体(move | | data
或just| | data
)意味着闭包获取捕获的值的所有权。反之则不适用:仅仅是您使用move
强制闭包接管值的所有权并不意味着调用将消耗该值。但是如果您愿意,您可以通过执行类似drop(data)的操作来强制执行该操作
在闭包体中,这将使您的闭包fn一次
为什么要从让audio\u buffer=audio\u buffer;
开始?我认为如果您放下它并正常使用外部的audio\u buffer
就可以了。仅仅放一行audio\u buffer;
会导致问题,这行的唯一目的是我s引用变量进行演示。但是您实际如何使用audio\u buffer
?如果调用使用外部值的对象,您将得到此错误。如果调用仅使用外部值引用的对象,我相信它会起作用。让audio\u buffer=audio\u buffer;
意味着从out作用域指向一个特定函数调用的作用域,这意味着该函数只能调用一次,否则相同的值将被移动多次。否,即使在//某些内容中没有任何内容,也会发生相同的错误。@sergmister OK。您能编辑您的问题以提供一个完整的、可编译的示例吗(最好是在中工作的)?当我们可以看到编译错误并探索更改的影响时,更容易精确。好的,我将这样做“这里,iter()是一个采用&self的方法。因此,数据不会被方法调用移出/消耗。”那么,如果数据未被使用,为什么不能在闭包后使用它呢?@sergmister它被移动到闭包中,因为您编写了move |
。如果没有move
,它将被借用。我编辑了我的答案,以进一步描述这一点。