40

How would I idiomatically create a string to string hashmap in rust. The following works, but is it the right way to do it? is there a different kind of string I should be using?

use std::collections::hashmap::HashMap;
//use std::str;
fn main() {
        let mut mymap = HashMap::new();
        mymap.insert("foo".to_string(), "bar".to_string());
        println!("{0}", mymap["foo".to_string()]);
}
1
  • 2
    Does it need to be owned strings? Can you do it with &'static str (string literals), especially for the keys? Commented Oct 12, 2014 at 9:57

2 Answers 2

36

Assuming you would like the flexibility of String, HashMap<String, String> is correct. The other choice is &str, but that imposes significant restrictions on how the HashMap can be used/where it can be passed around; but if it it works, changing one or both parameter to &str will be more efficient. This choice should be dictated by what sort of ownership semantics you need, and how dynamic the strings are, see this answer and the strings guide for more.

BTW, searching a HashMap<String, ...> with a String can be expensive: if you don't already have one, it requires allocating a new String. We have a work around in the form of find_equiv, which allows you to pass a string literal (and, more generally, any &str) without allocating a new String:

use std::collections::HashMap;
fn main() {
    let mut mymap = HashMap::new();
    mymap.insert("foo".to_string(), "bar".to_string());
    println!("{}", mymap.find_equiv(&"foo"));
    println!("{}", mymap.find_equiv(&"not there"));
}

playpen (note I've left the Option in the return value, one could call .unwrap() or handle a missing key properly).

Another slightly different option (more general in some circumstances, less in others), is the std::string::as_string function, which allows viewing the data in &str as if it were a &String, without allocating (as the name suggests). It returns an object that can be dereferenced to a String, e.g.

use std::collections::HashMap;
use std::string;

fn main() {
    let mut mymap = HashMap::new();
    mymap.insert("foo".to_string(), "bar".to_string());
    println!("{}", mymap[*string::as_string("foo")]);
}

playpen

(There is a similar std::vec::as_vec.)

Sign up to request clarification or add additional context in comments.

6 Comments

Could MaybeOwned allow some flexibility here? It seems it could be used as key?
I take it find_equiv() is no longer necessary? Your Playpen example doesn't compile anymore, and I don't see a find_equiv method on HashMap.
find_equiv() has been removed in favor of the Borrow trait. Conversion between &str and &String is done automatically now when using HashMap get/contains/remove methods. Working playpen here.
What exactly are the "significant restrictions" that using &str would place on the HashMap?
@Chad: Difficulty using any str with non-'static lifetimes. If it's keyed entirely with literal str with 'static lifetime, it's fine. If it's deriving those keys from existing Strings with more limited lifetimes, you need to ensure they stay alive at least as long as the HashMap, which is a royal pain.
|
16

Writing this answer for future readers. huon's answer is correct at the time but *_equiv methods were purged some time ago.

The HashMap documentation provides an example on using String-String hashmaps, where &str can be used.

The following code will work just fine. No new String allocation necessary:

use std::collections::HashMap;

fn main() {
    let mut mymap = HashMap::new();
    mymap.insert("foo".to_string(), "bar".to_string());
    println!("{0}", mymap["foo"]);
    println!("{0}", mymap.get("foo").unwrap());
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.