通过迭代从BTreeMap或BTreeSet中删除项目

时间:2015-10-02 18:17:19

标签: rust

我想删除BTreeMap中通过迭代找到的项目。

由于迭代时无法删除项目,因此我将要删除的项目放入向量中。主要问题是不能使用引用的向量,而只能使用值向量。必须克隆必须删除条目的所有密钥(假设密钥实现了Clone特征)。

例如,这个简短的示例无法编译:

use std::collections::BTreeMap;

pub fn clean() {
    let mut map = BTreeMap::<String, i32>::new();

    let mut to_delete = Vec::new();

    {
        for (k, v) in map.iter() {
            if *v > 10 {
                to_delete.push(k);
            }
        }
    }

    for k in to_delete.drain(..) {
        map.remove(k);
    }
}

fn main() {}

编译时会产生以下错误:

error[E0502]: cannot borrow `map` as mutable because it is also borrowed as immutable
  --> src/main.rs:17:9
   |
9  |         for (k, v) in map.iter() {
   |                       --- immutable borrow occurs here
...
17 |         map.remove(k);
   |         ^^^ mutable borrow occurs here
18 |     }
19 | }
   | - immutable borrow ends here

使用to_delete.push(k)更改to_delete.push(k.clone())会使此代码段正确编译,但如果必须克隆要删除的每个密钥,则代价非常高。

有更好的解决方案吗?

1 个答案:

答案 0 :(得分:3)

TL; DR:你不能。

就编译器而言,BTreeMap::remove的实现可能会这样做:

pub fn remove<Q>(&mut self, key: &Q) -> Option<V>
where
    K: Borrow<Q>,
    Q: Ord + ?Sized,
{
    // actual deleting code, which destroys the value in the set
    // now what `value` pointed to is gone and `value` points to invalid memory

    // And now we access that memory, causing undefined behavior
    key.borrow();
}

因此,编译器必须防止在集合发生变异时使用对值的引用。

要做到这一点,你需要像假设的光标&#34;集合的API。这将允许您遍历集合,返回一个特殊类型,其中包含集合的可变内部。此类型可以为您提供检查的参考,然后允许您删除该项目。

我可能从一个不同的方向看问题。我没有试图保留地图,而是创造了一个全新的地图:

use std::collections::BTreeMap;

pub fn main() {
    let mut map = BTreeMap::new();

    map.insert("thief", 5);
    map.insert("troll", 52);
    map.insert("gnome", 7);

    let map: BTreeMap<_, _> =
        map.into_iter()
        .filter(|&(_, v)| v <= 10)
        .collect();

    println!("{:?}", map); // troll is gone
}

如果您的条件在使结构唯一的字段上相等(也就是PartialEqHash中使用的唯一字段),则可以实现{{1对于你的类型,直接抓住/删除它:

Borrow

另见: