and_modify()or_insert()

Rust 下有两个mapBTreeMapHashMap

我们日常在使用 map 的时候经常会遇到这样的情景:

  1. let mut map = HashMap::new();
  2. // ...
  3. map[&key] += val;

C++Python 等等支持 map 结构的语言中,这样子的操作不会遇到任何问题,但是在Rust 中,这样的操作会有以下问题:

  1. key 不一定存在在 map 中。Rust 会对每个[] 操作做越界检查,因此 map[&key] 其实是相当于 C++ 中的 map.at(key)
  2. map[&key] 得到的是一个immutable 的引用,不可以直接加上一个 val

在之前,我一般使用这样的方式来解决这个问题:

  1. if let Some(e) = map.get_mut(&key) {
  2. *e += val;
  3. } else {
  4. map.insert(key, val);
  5. }

这么写也能解决问题,但是很长,而且绕了几个弯子,很难一眼看出代码的意图。

在参考了 这个提问 后,我发现了几个好用的“彩蛋”:and_modify()or_insert()

之所以说是彩蛋,是因为它藏得太隐蔽了。。。我在之前使用clippy处理我的代码的时候从未遇到过有关于此的提示。Rust 总是会有这样子的现象,你以为它缺少某个功能而不方便使用,但是其实它就在那里,得靠你自己去发现,然后它会告诉你,看,不是我不好用,只是你笨。

Orz

这两个函数很好地解决了以上的问题:

  1. map.entry(&key)
  2. .and_modify(|e| *e += val)
  3. .or_insert(val)

官方文档

此外还有几个类似的函数:

  1. or_default,只对实现了Default trait的值有用
  2. or_insert_with,和or_insert的区别是:or_insert插入一个值,而or_insert_with插入的是一个函数的返回值
  3. or_insert_with_kay,在or_insert_with的基础上,将key作为函数的参数传入