Skip to content

Interior mutability can cause inconsistency in collections #23327

@apasel422

Description

@apasel422

Collections that perform lookups based on a given property (e.g. a hash or an ordering) can be put into an inconsistent state when the property changes while its corresponding item is in the collection. This is normally not a problem, because such collections (at least those in std) do not provide mutable access to the type from which the property is derived (keys in the case of maps, items in the case of sets), but types with interior mutability can induce this behavior in safe code:

#[test]
fn test() {
    use std::cell::Cell;
    use std::collections::HashSet;
    use std::hash;

    #[derive(PartialEq)]
    struct Foo(Cell<u32>);

    impl Eq for Foo {}

    impl hash::Hash for Foo {
        fn hash<H: hash::Hasher>(&self, h: &mut H) { self.0.get().hash(h); }
    }

    let mut set = HashSet::new();
    set.insert(Foo(Cell::new(0)));
    set.insert(Foo(Cell::new(1)));

    for foo in &set {
        foo.0.set(2);
        break;
    }

    assert!(set.contains(&Foo(Cell::new(2))));
}

Output:

thread 'test' panicked at 'assertion failed: set.contains(&Foo(Cell::new(2)))'

This is obvious from the definition of interior mutability, but it's definitely a footgun. If the use of such keys cannot be prevented with the type system, the collections should at least document that the property must remain constant for the duration of the item's presence in the collection.

For example, Java's Map interface documentation contains the following note:

great care must be exercised if mutable objects are used as map keys. The behavior of a map is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is a key in the map.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions