来个大佬教教我,这个闭包体为什么是 FnOnce - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
Number13
V2EX    程序员

来个大佬教教我,这个闭包体为什么是 FnOnce

  •  
  •   Number13 2023-08-30 18:02:31 +08:00 1972 次点击
    这是一个创建于 860 天前的主题,其中的信息可能已经有所发展或是发生改变。

    下面这段代码中,闭包体为什么变成了 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); } 
    10 条回复    2023-08-31 14:33:09 +08:00
    hsfzxjy
        1
    hsfzxjy  
       2023-08-30 18:10:49 +08:00 via Android
    这段代码里闭包也是 FnMut 呀
    BBCCBB
        2
    BBCCBB  
       2023-08-30 18:27:22 +08:00
    应该就是所有权吧. value move 到了这个闭包里.
    xring
        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)
    ie88
        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"
    ie88
        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
    lsk569937453
        6
    lsk569937453  
       2023-08-30 20:32:39 +08:00   1
    Fn 、FnMut 、FnOnce 的区别建议看下 https://rustcc.cn/article?id=8b6c5e63-c1e0-4110-8ae8-a3ce1d3e03b9
    FreeWong
        7
    FreeWong  
       2023-08-31 10:58:16 +08:00
    sort_operations.push(value); 你自己要求要 push 一个 String 类型的 value, 然而 sort_by_key 对 闭包的要求是 FnMut ,即可以对周围环境变量的捕获是 Fn,FnMut ,所以就冲突了
    Number13
        8
    Number13  
    OP
       2023-08-31 14:04:29 +08:00
    @lsk569937453 666 ,我就是陷入了误解四,认为 String 是否 clone 会影响到闭包体是否为 FnOnce 。
    @ie88 哦哦,是这样啊,这块我想到了,但是我以为这个所有权是给了 vec 看你解释,明白了一些了,是闭包体把 vec 和这个 String 的所有权都捕获了吧。
    PTLin
        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`。
    PTLin
        10
    PTLin  
       2023-08-31 14:33:09 +08:00
    我把你的代码改写成了底层形式,你看看是不是一样的错误
    https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=9c32461c6ece185b284827fd296cc1c9
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5409 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 03:20 PVG 11:20 LAX 19:20 JFK 22:20
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86