Removing items from a BTreeMap or BTreeSet found through iteration

TL;DR: you cannot.

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.

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
}

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
}

See also:

Leave a Comment