从通过迭代找到的 BTreeMap 或 BTreeSet 中删除项目 [英] Removing items from a BTreeMap or BTreeSet found through iteration

查看:31
本文介绍了从通过迭代找到的 BTreeMap 或 BTreeSet 中删除项目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I would like to remove items from a BTreeMap which have been found through iteration.

由于迭代时无法删除项目,我将要删除的项目放入向量中.主要问题是不可能使用引用的向量,而只能使用值的向量.然后必须克隆所有必须删除条目的键(假设该键实现了 Clone trait).

As it is not possible to remove items while iterating, I put the items to delete into a vector. The main issue is that it is not possible to use a vector of references, but only a vector of values. All the keys for which the entry has to be removed must then be cloned (assuming the key implements the Clone trait).

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

For instance, this short sample does not compile:

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() {}

它在编译时产生以下错误:

It generates the following errors when it is compiled:

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.clone()) 更改 to_delete.push(k) 可以使此代码段正确编译,但如果要删除每个键,则成本会很高必须克隆.

Changing to_delete.push(k) with to_delete.push(k.clone()) makes this snippet compile correctly but it is quite costly if each key to delete must be cloned.

有更好的解决方案吗?

推荐答案

TL;DR:你不能.

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

As far as the compiler is concerned, the implementation of BTreeMap::remove might do this:

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();
}

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

The compiler thus has to prevent using the reference to the value when the collection will be mutated.

要做到这一点,您需要类似于集合的假设光标"API.这将允许您遍历集合,返回一个特殊类型,该类型保存集合的可变内部.这种类型可以为您提供一个参考以进行检查,然后允许您删除该项目.

To do this, you'd need something like the hypothetical "cursor" API for collections. This would allow you to iterate over the collection, returning a special type that hold the mutable innards of the collection. This type could give you a reference to check against and then allow you to remove the item.

我可能会从稍微不同的方向看问题.我不想保留地图,而是创建一个全新的地图:

I'd probably look at the problem from a bit different direction. Instead of trying to keep the map, I'd just create a brand new one:

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 中使用的唯一字段),你可以为你的类型实现 Borrow 并直接抓取它/删除它:


If your condition is equality on the field that makes the struct unique (a.k.a is the only field used in PartialEq and Hash), you can implement Borrow for your type and directly grab it / delete it:

use std::collections::BTreeMap;

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
struct Monster(String);

use std::borrow::Borrow;

impl Borrow<str> for Monster {
    fn borrow(&self) -> &str { &self.0 }
}

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

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

    map.remove("troll");

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

另见:

这篇关于从通过迭代找到的 BTreeMap 或 BTreeSet 中删除项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆