下面这段代码中,闭包体为什么变成了 FnOnce ?是因为 Value 被 push 到 vec 中,导致所有权转移,所以 rust 认为它是 FnOnce 的闭包体了么?还是因为什么?求解答
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let mut list = [
Rectangle { width: 10, height: 1 },
Rectangle { width: 3, height: 5 },
Rectangle { width: 7, height: 12 },
];
let mut sort_operations = vec![];
let value = String::from("by key called");
list.sort_by_key(|r| {
sort_operations.push(value); // 如果这里改成 sort_operations.push(value.clone());
// 那么这个闭包体就是 FnMut 类型了
r.width
});
println!("{:#?}", list);
}
1
hsfzxjy 2023-08-30 18:10:49 +08:00 via Android
这段代码里闭包也是 FnMut 呀
|
2
BBCCBB 2023-08-30 18:27:22 +08:00
应该就是所有权吧. value move 到了这个闭包里.
|
3
xring 2023-08-30 19:55:16 +08:00
Fn: the closure uses the captured value by reference (&T)
FnMut: the closure uses the captured value by mutable reference (&mut T) FnOnce: the closure uses the captured value by value (T) |
4
ie88 2023-08-30 20:10:47 +08:00
你这段代码会报错,你从哪里看到 FnMut 变成了 FnOnce ?
想要实现查看 sort_by_key 执行多少次,把 String::from() 换成 &str , 即 let value = "by key called"; 编译器已经提示你 "move occurs because `value` has type `String`, which does not implement the `Copy` trait" |
5
ie88 2023-08-30 20:19:26 +08:00
@ie88 再详细点说,就是 这个 value (String 类型) 第一次被 push 到 sort_operations 这个 Vec 里,就已经被 move 到了 closure ,在这个作用域内已经没有了这个 value ,所以下一次 进行 push 操作时,找不到这个 value 了,因为 你定义的 value 是 String 类型,不具有 Copy trait
如果你 定义 value 时,像我上面写的,用 &str ,move 到 closure 时就会隐式发生 copy |
6
lsk569937453 2023-08-30 20:32:39 +08:00 1
Fn 、FnMut 、FnOnce 的区别建议看下 https://rustcc.cn/article?id=8b6c5e63-c1e0-4110-8ae8-a3ce1d3e03b9
|
7
FreeWong 2023-08-31 10:58:16 +08:00
sort_operations.push(value); 你自己要求要 push 一个 String 类型的 value, 然而 sort_by_key 对 闭包的要求是 FnMut ,即可以对周围环境变量的捕获是 Fn,FnMut ,所以就冲突了
|
8
Number13 OP @lsk569937453 666 ,我就是陷入了误解四,认为 String 是否 clone 会影响到闭包体是否为 FnOnce 。
@ie88 哦哦,是这样啊,这块我想到了,但是我以为这个所有权是给了 vec 看你解释,明白了一些了,是闭包体把 vec 和这个 String 的所有权都捕获了吧。 |
9
PTLin 2023-08-31 14:13:23 +08:00
闭包可以看作一个结构体,你对闭包的使用方式决定了闭包的捕获方式,捕获方式决定了闭包实现了什么 trait 。
假设有个结构体,你这段代码将 value push 到了 vec 中,故捕获了&mut vec 。value 是所有权方式使用的,故捕获了 string 。 这时这个闭包的结构体中就有两个字段,分别是&mut vec ,value 。 调用 FnOnce 闭包在底层相当于调用了闭包结构体的 fn call_once(self, args: Args) -> Self::Output 方法。 调用 FnMut 闭包在底层相当于调用了闭包结构体的 fn call_mut(&mut self, args: Args) -> Self::Output 方法。 由此可知假设你传入的闭包实现了 FnMut ,此时将会调用 fn call_mut(&mut self, args: Args) -> Self::Output 。这时就会出现问题,代码中将 string 类型的 value ,push 到了&mut vec 中,但由于 self 是&mut ,无法对 value 转移出所有权,所以 error 的提示为[E0507]: cannot move out of `value`。 |
10
PTLin 2023-08-31 14:33:09 +08:00
|