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

2022-03-05 16:29:42 +08:00
 chuanqirenwu

代码出自 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()
}

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

3407 次点击
所在节点    Rust
7 条回复
palfortime
2022-03-05 18:02:26 +08:00
map 只是一个&,不是&mut ,不可能发生 move ,Progress 应该实现了 Copy 吧。
chuanqirenwu
2022-03-05 18:22:04 +08:00
@palfortime 是的,那这么来看所有的操作都是引用了,就是不知道各种写法间细微的区别是什么样的了。
gfreezy
2022-03-05 20:26:25 +08:00
实际使用都是引用,没啥区别。pattern match 的时候编译器会自动加&
chuanqirenwu
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
2022-03-06 00:28:06 +08:00
`|(_, v)| *v == &value`
`|&(_, v)| v == &value`
`|(_, v)| v == &&value`

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

我大概理解,从指针的地址来看,确实 m2 已超过栈顶范围,但在我的电脑环境下,取 m2 指向的地址里的值,仍然可以取到,所以得到了帖子中说的结果。但如几位大佬所说,这是一个 ub 。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/838203

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX