Immutable slices of mutable references

I was trying to write a function that would remove a key from a slice of hashmaps if that key exists in all the hashmaps.

I started with something like this:

fn remove_common_k(maps: &[&mut HashMap<String, String>]) {
    let all_have_key = maps.iter().all(|m| m.contains_key("thekey"));

    if all_have_key {
        for map in maps {
            map.remove("thekey")
        }
    }
}

map is of type &&mut HashMap<String, String>, and the remove statement is an error because it is "behind an immutable reference". I wasn't able to dereference this to just &mut HashMap<String, String>.

My naive thought was that, although the slice itself is immutable, the values are mutable references, so it should be safe to modify the content of map because the actual reference to map is not changing.

But then, thinking further, I considered that since maps is an immutable reference, and you're allowed to have as many immutable references as you like, that if you could get a mutable reference from inside an immutably referenced slice, you'd be able to violate the rule about only having a single mutable reference like this:

fn remove_common_vals(maps: &[&mut HashMap<String, String>]) {
    let maps1 = maps;
    let maps2 = maps;
    // Rust won't like this:
    let map1 = maps1[0];
    let map2 = maps2[0];
    // because now I have two mutable references to the same data.
}

Therefore, Rust can't allow you to mutate something through an immutable reference (ie., something mutable inside something immutable, even if it's a reference to somewhere else).

My questions are:

  1. Am I thinking about this in the right way?
  2. Does it ever make sense to have a mutable reference inside of an immutable reference?
    b. If not, should there be a clippy about this not having utility?
  3. What should the signature of such a function look like? &mut[&mut HashMap<String, String>]?
1 Like
  1. Yes
  2. Not really
  3. Yes
3 Likes

Yes!

To be more precise, because & is not exclusive, you can't get any exclusive access to the contents.

(If Rust had an “immutable and exclusive” reference type, then it could allow you to access then &muts inside the slice without also being able to mutate the slice itself, but it doesn't. But while such references don't exist, that kind of access does crop up occasionally, such as in the fact that putting a &mut reference in a variable doesn't require you to declare the variable mut to use it.)

Does it ever make sense to have a mutable reference inside of an immutable reference?

Yes, it allows you to have immutable/shared access to the contents of (in this case) the slice without needing to construct a new vector of immutable references. In general, there's no reason to make &[&mut T] from scratch, but if you already have [&mut T] (whether by reference &[&mut T], or ownership Vec<&mut T>), there might be reason to take a &[&mut T] reference to it.

What should the signature of such a function look like? &mut[&mut HashMap<String, String>]?

Yes.

7 Likes