在Rust中如何使用BTreeMap存储HashSet并修改其中的HashSet元素
刚接触Rust的话,这种嵌套集合的可变借用问题确实容易踩坑,我来帮你拆解下你的两个尝试为啥出错,再给你靠谱的解决办法~
首先看你第一次尝试的问题:
你写了map.entry(1).and_modify(|s| *s.insert(7));,这里的错误是HashSet::insert方法返回的是一个bool值(用来表示这个元素是不是第一次被插入到集合里),而你试图对这个bool值做解引用(*操作),这完全没必要,而且Rust里bool根本不能被解引用,所以直接报错了。实际上and_modify闭包里的s已经是&mut HashSet<i32>类型的可变引用了,你直接调用s.insert(7)就可以,不需要额外的解引用操作。
再看第二次尝试的问题:
你用了map.get(&1),这个方法返回的是Option<&HashSet<i32>>,也就是不可变引用的Option。哪怕你在match里写了ref mut hashset,也没法把一个不可变引用转换成可变引用,Rust的借用规则不允许这么做。你应该用map.get_mut(&1),它返回的是Option<&mut HashSet<i32>>,这样才能拿到可以修改的引用。
接下来给你两种正确的写法,都是Rust里处理这种场景的常规操作:
方法一:推荐用Entry API(更简洁,还能处理key不存在的情况)
Entry API是Rust集合类型里专门用来处理“存在就修改,不存在就创建”场景的利器,用它处理嵌套集合非常顺手:
use std::collections::{BTreeMap, HashSet}; fn main() { let mut map: BTreeMap<i32, HashSet<i32>> = BTreeMap::new(); // 先给key=1插入一个空HashSet map.insert(1, HashSet::new()); // 修改已存在的key对应的HashSet map.entry(1) .and_modify(|mutable_set| { // mutable_set是&mut HashSet<i32>,直接调用insert即可 // 如果需要的话,你也可以用insert返回的bool做判断,比如: // let was_new = mutable_set.insert(7); // if was_new { println!("元素7是新插入的"); } mutable_set.insert(7); }); // 进阶:如果key不存在,自动创建空HashSet并插入元素 // 比如处理key=2,不管之前有没有,最后都会有一个包含8的HashSet map.entry(2) .or_insert_with(HashSet::new) .insert(8); }
方法二:用get_mut直接获取可变引用
如果你确定要修改的key一定存在,或者只想处理存在的key,用get_mut也很直接:
use std::collections::{BTreeMap, HashSet}; fn main() { let mut map: BTreeMap<i32, HashSet<i32>> = BTreeMap::new(); map.insert(1, HashSet::new()); // 尝试获取key=1的可变引用,如果存在就插入元素 if let Some(mutable_set) = map.get_mut(&1) { mutable_set.insert(7); } // 如果key不存在,就什么都不做,或者你也可以在这里插入新的HashSet }
你之前用Vec的时候能成功,是因为Vec::push方法返回的是()(空单元类型),所以你之前的写法不会触发解引用错误,但HashSet的insert返回bool,所以多了个解引用就出问题了。现在用上面的写法,就能顺利修改BTreeMap里的HashSet元素啦。
内容来源于stack exchange

