Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Rust BTreeMap becomes invalid when key is mutated

In the BTreeMap values are sorted with the Ord trait. However, a key can change one of its attributes considered in the Ord trait implementation as shown in the example. This can lead to the BTreeMap containing values that can no longer be reached with the corresponding key because they are in the wrong place. Is it possible to work around this somehow and have such a value automatically moved to the right place when it is changed or is there perhaps another data structure that avoids the problem?

fn main() {
  use std::collections::BTreeMap;
  use std::cell::RefCell;

  #[derive(Ord, PartialOrd, PartialEq ,Eq, Debug)]
  struct Key {
    value: RefCell<usize>
  }
  impl Key {
    pub fn new(x: usize) -> Self {
      Self {
        value: RefCell::new(x)
      }
    }
  }

  //create a tree(bool value does not have any specific purpose)

  let mut a = BTreeMap::new();
  a.insert(Key::new(3), false);
  a.insert(Key::new(2), false);
  a.insert(Key::new(4), false);
  a.insert(Key::new(5), false);

  //tree looks like this:
  //        3
  //       / \
  //      2   4
  //           \
  //            5


  *a.get_key_value(&Key::new(5)).unwrap().0.value.borrow_mut() = 1;

  //mutated invalid tree:
  //        3
  //       / \
  //      2   4
  //           \
  //            1

  for x in a.keys() {
    println!("{:?}", x);
  }
  
  println!("{:?}", a.get(&Key::new(1)))
}

output:

Key { value: RefCell { value: 2 } }
Key { value: RefCell { value: 3 } }
Key { value: RefCell { value: 4 } }
Key { value: RefCell { value: 1 } }
None

As you can see here, the value 1 exists as a key in the BTreeMap, but cannot be found.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

Rust BTreeMap becomes invalid when key is mutated

This is documented:

It is a logic error for a key to be modified in such a way that the key’s ordering relative to any other key, as determined by the Ord trait, changes while it is in the map.

Is it possible to work around this somehow and prevent such a value from being automatically moved to the right place when it is changed

It’s not moved at all, which is why you’re breaking the map when you do that.

Clippy has a lint which warns about such misuses.

Though it can not be perfect (it has both false positives and false negatives), here if you plug your code in the playground and select Tools > Clippy, you will see:

warning: mutable key type
  --> src/main.rs:19:3
   |
19 |   let mut a = BTreeMap::new();
   |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: `#[warn(clippy::mutable_key_type)]` on by default
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type

or is there perhaps another data structure that avoids the problem?

The map can not be aware that you’re mutating keys (despite being told not to do that). There is literally no way for the language to prevent this, short of only allowing a specific built-in immutable key type.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading