struct Data {
name: String,
id: u32,
}
impl Data {
fn getval(&self, val: Vec<u32>) -> u32 {
self.id+val[0]
}
}
fn call_closue<T>(data: &Data, f: T) -> u32
where T: Fn(&Data)->u32 {
f(data)
}
fn test(data: &Data, val: Vec<u32>) {
let _ret = call_closue(data, move |person: &Data|person.getval(val));
}
fn main() {
let a = Data{name:"a1".to_string(), id:23};
let a1 = vec![11];
test(&a, a1);
}
编译报错,没太理解为啥, 这里用 move就是想要获取vec的所有权。
error[E0507]: cannot move out of `val`, a captured variable in an `Fn` closure
--> src/main.rs:46:68
|
45 | fn test(data: &Data, val: Vec<u32>) {
| --- captured outer variable
46 | let _ret = call_closue(data, move |person: &Data|person.getval(val));
| -------------------- ^^^ move occurs because `val` has type `Vec<u32>`, which does not implement the `Copy` trait
| |
| captured by this `Fn` closure
For more information about this error, try `rustc --explain E0507`
1
共 6 条评论, 1 页
评论区
写评论要感谢rust的严格,反着分析下,假如Fn可以,那单从函数签名上就表明,下面这个函数f可以调用多次
然而,传进来f真的能调用多次吗?传过来的f是下面这个move,val所有权被闭包捕获,它能调用多次吗?
感觉能调用多次,然而getval的签名,是直接消费了val!这里容易被忽略
看getval的签名,如果调用一次就把val消费了,还想调用第二次、第三次,val都被销毁了,肯定不行了,所以违反Fn,只能用FnOnce
若想Fn也可以,getval的参数val要是不可变引用,闭包里面调用getval的时候也要使用person.getval(&val),即可
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=086259828b674a7a319451d64008560b
一个闭包实现了哪种 Fn 特征取决于该闭包如何使用被捕获的变量,而不是取决于闭包如何捕获它们 你的代码里
上面val被闭包捕获, 调用data的getval方法, getval方法签名中val被获取了所有权 所以此闭包是一个FnOnce, 此时就算把move去掉也不影响此闭包的行为
其实你的代码中根本不必获得val的所有权,
self.id+val[0]
这里你只是不可变的借用val 所以我建议你这样修改:test 函数里面拿到了 Val 的所有权,所以要么只能调用一次(FnOnce),也可以通过传引用的方式解决这个问题:
你这个代码的完整形式是这样的,看看Fn和FnOnce的call签名,就知道为什么报错是E0507以及为什么改成FnOnce能通过了。
--
👇
disheng727: FnOnce是调用一次的闭包,但是代码里,并没有多次调用闭包, 改成FnOnce 为什么就可以了。。
--
👇
Bai-Jinlin: Fn -> FnOnce
FnOnce是调用一次的闭包,但是代码里,并没有多次调用闭包, 改成FnOnce 为什么就可以了。。
--
👇
Bai-Jinlin: Fn -> FnOnce
Fn -> FnOnce