我想删除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() {}
Run Code Online (Sandbox Code Playgroud)
它在编译时会产生以下错误:
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
Run Code Online (Sandbox Code Playgroud)
改变to_delete.push(k)以to_delete.push(k.clone())使这个片断编译正确,但它是相当昂贵的,如果每个键删除必须被克隆.
有更好的解决方案吗?
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();
}
Run Code Online (Sandbox Code Playgroud)
因此,当集合发生变异时,编译器必须防止使用对值的引用。
为此,您需要类似假设的“游标”集合 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
}
Run Code Online (Sandbox Code Playgroud)
如果您的条件在使结构唯一的字段上相等(也就是PartialEqand 中使用的唯一字段Hash),您可以Borrow为您的类型实现并直接获取/删除它:
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
}
Run Code Online (Sandbox Code Playgroud)
也可以看看:
| 归档时间: |
|
| 查看次数: |
1343 次 |
| 最近记录: |