Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does one create a HashMap with a default value in Rust?

Tags:

rust

Being fairly new to Rust, I was wondering on how to create a HashMap with a default value for a key? For example, having a default value 0 for any key inserted in the HashMap.

In Rust, I know this creates an empty HashMap:

let mut mymap: HashMap<char, usize> = HashMap::new();

I am looking to maintain a counter for a set of keys, for which one way to go about it seems to be:

for ch in "AABCCDDD".chars() {
    mymap.insert(ch, 0)
}

Is there a way to do it in a much better way in Rust, maybe something equivalent to what Ruby provides:

mymap = Hash.new(0)
mymap["b"] = 1
mymap["a"] # 0
like image 541
hindenbug Avatar asked Jan 01 '17 17:01

hindenbug


People also ask

How does a HashMap work rust?

Hashmap in rust is a structure which comprises of the look-up table and data in it in form of key and value pair which will be used for storing and retrieving data continuously. Hashmap needs to be explicitly imported from the rust inbuilt library collection before that can be used within the program.

Are Hashmaps ordered rust?

HashMap s are inherently unordered collections, and BTree s are inherently ordered by their keys.

What are Hashmaps good for?

Hashmaps are probably the most commonly used implementation of the concept of a map. They allow arbitrary objects to be associated with other arbitrary objects. This can be very useful for doing things like grouping or joining data together by some common attribute.


2 Answers

Answering the problem you have...

I am looking to maintain a counter for a set of keys.

Then you want to look at How to lookup from and insert into a HashMap efficiently?. Hint: *map.entry(key).or_insert(0) += 1


Answering the question you asked...

How does one create a HashMap with a default value in Rust?

No, HashMaps do not have a place to store a default. Doing so would cause every user of that data structure to allocate space to store it, which would be a waste. You'd also have to handle the case where there is no appropriate default, or when a default cannot be easily created.

Instead, you can look up a value using HashMap::get and provide a default if it's missing using Option::unwrap_or:

use std::collections::HashMap;

fn main() {
    let mut map: HashMap<char, usize> = HashMap::new();
    map.insert('a', 42);

    let a = map.get(&'a').cloned().unwrap_or(0);
    let b = map.get(&'b').cloned().unwrap_or(0);

    println!("{}, {}", a, b); // 42, 0
}

If unwrap_or doesn't work for your case, there are several similar functions that might:

  • Option::unwrap_or_else
  • Option::map_or
  • Option::map_or_else

Of course, you are welcome to wrap this in a function or a data structure to provide a nicer API.


ArtemGr brings up an interesting point:

in C++ there's a notion of a map inserting a default value when a key is accessed. That always seemed a bit leaky though: what if the type doesn't have a default? Rust is less demanding on the mapped types and more explicit about the presence (or absence) of a key.

Rust adds an additional wrinkle to this. Actually inserting a value would require that simply getting a value can also change the HashMap. This would invalidate any existing references to values in the HashMap, as a reallocation might be required. Thus you'd no longer be able to get references to two values at the same time! That would be very restrictive.

like image 119
Shepmaster Avatar answered Oct 08 '22 18:10

Shepmaster


What about using entry to get an element from the HashMap, and then modify it.

From the docs:

fn entry(&mut self, key: K) -> Entry<K, V>

Gets the given key's corresponding entry in the map for in-place manipulation.

example

use std::collections::HashMap;

let mut letters = HashMap::new();

for ch in "a short treatise on fungi".chars() {
    let counter = letters.entry(ch).or_insert(0);
    *counter += 1;
}

assert_eq!(letters[&'s'], 2);
assert_eq!(letters[&'t'], 3);
assert_eq!(letters[&'u'], 1);
assert_eq!(letters.get(&'y'), None);
like image 13
Akavall Avatar answered Oct 08 '22 19:10

Akavall