V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
chuanqirenwu
V2EX  ›  Rust

Rust 这 4 种写法都通过编译了,但不知道我这样理解是对的吗?

  •  
  •   chuanqirenwu · 2022-03-05 16:29:42 +08:00 · 3394 次点击
    这是一个创建于 995 天前的主题,其中的信息可能已经有所发展或是发生改变。

    代码出自 rustlings:

    第一种:

    fn count_iterator(map: &HashMap<String, Progress>, value: Progress) -> usize {
        // 文档中说 iter 返回 (&'a K, &'a V),也就是 map 的 K 和 V 的引用组成的 tuple 。
        // 然后这个 tuple 被 deconstruct 了,且 V move 给了 v ,顺带地 map 也被 move 了?
        map.iter().filter(|(_, &v)| v == value).count()
    }
    

    第二种:

    fn count_iterator(map: &HashMap<String, Progress>, value: Progress) -> usize {
        // 无法理解为什么要 2 次 deref
        map.iter().filter(|(_, v)| **v == value).count()
    }
    

    第三种:

    fn count_iterator(map: &HashMap<String, Progress>, value: Progress) -> usize {
        // 请问这里 value 也是 move 给了 v 吗?那和第一种的区别是?
        map.iter().filter(|&(_, &v)| v == value).count()
    }
    

    第四种:

    fn count_iterator(map: &HashMap<String, Progress>, value: Progress) -> usize {
    	// 首先 &(_, v) borrow map 的 (&'a K, &'a V),然后这个 tuple 被 deconstruct ,v 是
        // &'a V ,所以 deref v 拿到值,从而和 value 参数的值进行比较
        map.iter().filter(|&(_, v)| *v == value).count()
    }
    

    理解上可能有误,还请大佬们指正,谢谢!

    7 条回复    2022-04-04 18:10:34 +08:00
    palfortime
        1
    palfortime  
       2022-03-05 18:02:26 +08:00 via Android
    map 只是一个&,不是&mut ,不可能发生 move ,Progress 应该实现了 Copy 吧。
    chuanqirenwu
        2
    chuanqirenwu  
    OP
       2022-03-05 18:22:04 +08:00
    @palfortime 是的,那这么来看所有的操作都是引用了,就是不知道各种写法间细微的区别是什么样的了。
    gfreezy
        3
    gfreezy  
       2022-03-05 20:26:25 +08:00 via iPhone
    实际使用都是引用,没啥区别。pattern match 的时候编译器会自动加&
    chuanqirenwu
        4
    chuanqirenwu  
    OP
       2022-03-05 23:51:54 +08:00
    经过一番调查我可能大致理解了,先来看不带任何 & 的情况,
    即:`map.iter().filter(|(_, v)| **v == value).count()`。首先 filter 会以引用的形式去迭代 map.iter() 返回的类型(&K, &V ),因此 filter 迭代的类型是 &(&K, &V),此类型被 (_, v) match ,因此 v 具有类型 &&V ,如果我们调用 V 的方法,由于 rust 的 deref coercions ,这是没有问题的,但如果和不一致的类型比较,就会报错,想取到 v 的值,需要 2 次 deref ,即 **v 。

    再来看:`map.iter().filter(|&(_, v)| *v == value).count()`,根据前面的分析,&(_, v) pattern 帮忙脱掉了一层 &,因此 v 的类型变为 &V 。

    再来看:`map.iter().filter(|(_, &v)| v == value).count()`,第二个位置的参数匹配的值的类型为 &&V ,匹配模式为 &v ,由于 rust 的 deref coercions ,变为 &V 匹配 &v ,V move or copy to v 。

    最后 `map.iter().filter(|&(_, v)| *v == value).count()`,第二个参数匹配的值的类型为 &V ,因此 v 的值为 &V ,做一次 deref 取得 V 的值。

    如此,所有情况都解释的通了,而且分析发现最好的写法应该是:`map.iter().filter(|&(k, v)| *v == value).count()`,这样 k 的类型是 &K ,v 是&V ,不存在 move 或者 copy 的情况。
    gydi
        5
    gydi  
       2022-03-06 00:28:06 +08:00   ❤️ 1
    `|(_, v)| *v == &value`
    `|&(_, v)| v == &value`
    `|(_, v)| v == &&value`

    这样也不用 copy
    macrorules
        6
    macrorules  
       2022-04-04 17:49:54 +08:00   ❤️ 1
    新注册的号不能连续回复,所以发个粘贴板:dpaste.org/Aa6k9oD
    chuanqirenwu
        7
    chuanqirenwu  
    OP
       2022-04-04 18:10:34 +08:00
    @macrorules 非常感谢,不过这个帖子时 rust 的,你这个粘贴板的内容应该对应这个帖子:/t/844896

    我大概理解,从指针的地址来看,确实 m2 已超过栈顶范围,但在我的电脑环境下,取 m2 指向的地址里的值,仍然可以取到,所以得到了帖子中说的结果。但如几位大佬所说,这是一个 ub 。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5136 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 03:57 · PVG 11:57 · LAX 19:57 · JFK 22:57
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.