How to create an immutable hash map?

Functional languages like Haskell, Scala or Kotlin provide easy ways of creating immutable maps, for instance mapOf.

Rust supports immutability as well, but it does not seem to provide a hash map implementation which can be used without changing its entries after creation. Is it true that I cannot create and fill a hash map using pure functions only? If not: How can I create an immutable hash map?

There you go. https://en.wikipedia.org/wiki/Hash_array_mapped_trie

Sorry I misunderstood your question. If by create you mean returning an immutable hashmap, you can just bind the created map to any variable. It is immutable by default.

In your first answer you suggest a crate. That is not what I expected since in the other mentioned languages the immutable map is part of the language standard libraries.

Can you post an example code snippet of turning a list (Vec) of key+value pairs into a hash map without using impure functions? (like Kotlin's mapOf)

 let map: HashMap<&str, i32> =
    [("roo", 1),
     ("bar", 2),
     ("baz", 3)]
    .iter().cloned().collect();

In Rust, largely due to guarantees of non-breakage and forever future compatibility, the language standard libraries are intentionally minimal, consisting primarily of those things that require special support in the compiler. Other functionality is usually provided by open-source crates that can (and do) explore different tradeoffs in the associated design space.

2 Likes

If you really never intend to mutate the map since you know all its elements at compile-time, I recommend you use the ::phf crate:

[dependencies]
phf = { version = "0.8.0", features = [ "macros" ] }
use ::phf::{Map, phf_map};

// `const` or `static` when known at compile-time, and providing
// shared access only (`&_` references), i.e., "immutable".
const MAP: Map<&str, i32> = phf_map! {
    "roo" => 1,
    "bar" => 2,
    "baz" => 3,
};
3 Likes

Thank you. That is what I was searching for.

However, the map is still mutable which is unnecessary and even dangerous, because it should not be changed after creation. It is meant to be a read-only view that makes possible fast access to records through keys. A read-only map such as Map (standard map of Kotlin) could tell the reader of my code how they are supposed to use the map (that is: never changing it because it is meant to be protected from function side effects for easier reasoning about the code).

I'm not sure what you mean by this... the map in @Congee's example is definitely not mutable. (That would have required let mut.)

HashMap is a data structure that can support mutation, but if its owner decides it shall not be mutated, then it's an immutable map. You can distribute &HashMap<...> references or Arc<HashMap<...>> or whatever without any risk of mutation.

This is an important difference between Rust and all of the languages you cite in your post: mutability vs immutability is a property of the value's context, not the type. There is no need for a separate immutable HashMap type (unless you want compile-time known-key optimizations like perfect hashing), and there's no need for a distinction like Kotlin's Map vs MutableMap.

4 Likes