Is there a way to create a constant map in Rust?

I'm assuming the answer is "No", because it seems like you can't do any allocation to set up a constant. What's the best way to do this/approximate this? I was thinking something super ugly like creating a struct that has a get_map_const() function that returns the 'constant' value. Is there a cleaner alternative?

(Note that I can't avoid the need for a map constant - but I can come up with different representations for it.)

5 Likes

You mean, a static HashMap? No, it isn't possible, because Rust does not (yet) support compile-time evaluation of code of such complexity as would be needed to implement a hashmap.

What are you trying to do that requires a static hashmap?

Edit: Actually, you might want to look into something like this: https://crates.io/crates/phf

3 Likes

Ah. I figured. And no - haven't taken a look at phf; will look tonight. As for what I'm writing: it's a code-generator to a spec, and the spec defines map literals that have to be translated to the target language.

Sounds like a PHF would be a perfect fit for that.

2 Likes

You can also use the lazy_static crate for this.

8 Likes

What about just a function with a single match statement? Would that fit your use case?

5 Likes

Thanks guys - I'll take a look at these options.

Just for those who happen to bump into this very old and persistent issue...

lazy_static : requires pulling in a new crate, although you probably have it anyway. And all those insert statements look... er...

phf : perfect, except requires nightly.

Recently I have been doing it this way, in preparation for eventually moving to phf:

// This map must be sorted
static MAP: &[(K, V)] = &[
    (K1, V1),
    (K2, V2),
    (K3, V3)
         :
];

// Use binary search to access the map:
pub fn get_V_from_K(key: K) -> V {
    MAP.binary_search_by(|(k, _)| k.cmp(&key)).map(|x| MAP[x].1)
}

If this is done a lot of times, a simple macro can be written to make the syntax look almost exactly like phf for seamless migration later on.

3 Likes
lazy_static!{
    static ref MAP: HashMap<char, &'static str> = vec![
        ('a', "apple"),
        ('b', "bear"),
        ('c', "cat"),
    ].into_iter().collect();
}
7 Likes

:clap: :clap: :clap:

This avoids an allocation:

lazy_static!{
    static ref MAP: HashMap<char, &'static str> = [
        ('a', "apple"),
        ('b', "bear"),
        ('c', "cat"),
    ].iter().copied().collect();
}
3 Likes
original post

They are about to release phf 0.8.0 to crates.io, which will feature, among other things, macros working on stable Rust. If you just want the stable macros, you can currently get them by putting

[dependencies]
phf = { git = "https://github.com/sfackler/rust-phf", rev = "6e1f6ac9b1f9", features = ["macros"] }

in your Cargo.toml

Starting from version 0.8.0, ::phf features these macros on stable Rust:

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

static MAP: Map<char, &'static str> = phf_map! {
    'a' => "apple",
    'b' => "bear",
    'c' => "cat",
};

EDIT: Updated post to no longer use a github revision but an actual crates.io version instead

13 Likes

@Yandros it works!!! Perfect! :clap:

1 Like

Actually it can be done just by Option (unsafe) like below(or view in playground):

use std::collections::HashMap;

static mut MAP: Option<HashMap<Box<String>, Box<String>>> = None;

fn init() {
    unsafe {
        MAP = Some(HashMap::new());
    }
}

fn set(key: String, val: String) {
    unsafe {
        MAP.as_mut().unwrap().insert(
            Box::new(String::from(key)),
            Box::new(String::from(val))
        );
    }
}

fn get(key: String) -> String {
    unsafe {
        return String::from(MAP.as_ref().unwrap().get(&key).unwrap().as_str());
    }
}

fn main() {
    init();
    set("a".to_string(), "apple".to_string());
    print!("{}", get("a".to_string())); // apple
}

No need to answer to a 5 years old topic, 2 years after the last post with an obviously unsound (since it isn’t thread-safe) new solution :wink:

Also, I would recommend anyone who isn’t at least somewhat decently familiar with Rust to avoid usage of unsafe in general, if possible. (I’m taking things like unnecessarily boxing of String as an indicator for the level language familiarity here.)

6 Likes

This topic was automatically closed 7 days after the last reply. We invite you to open a new topic if you have further questions or comments.