1

We have a static list of objects. We would like to create a static constant HashMap with the key being the names of the objects.

How do we do that?

As an example, here is the definition of a Book object:

struct Book<'a> {
    name: &'a str,
    author: &'a str,
    id: i32,
}

Here is a static list of 3 books:

const ALL_BOOKS: &'static [Book] = &[
    Book {
        name: "Zero to One",
        author: "James",
        id: 2,
    },
    Book {
        name: "Zero to One",
        author: "Tom",
        id: 1,
    },
    Book {
        name: "Turtle all the way",
        author: "Jerry",
        id: 1,
    },
];

We want to defined a dictionary of books with the key being the name of the book, and the value being a list of book with that name.

I notice that we have lazy_static from:

Here is what I tried:

lazy_static! {
    static ref ALL_BOOKS_BY_SHORT_NAME: HashMap<String, Vec<Book<'static>>> = {
        let mut map = HashMap::new();

        for book in ALL_BOOKS {
            if !map.contains_key(book.name) {
                map.insert(book.name.to_owned(), vec![book]);
            } else {
                let mut list = map.get(book.name).unwrap();
                list.push(book)
            }
        }

        map
    };
}

Here is the error:

error[E0308]: mismatched types
  --> src/main.rs:42:9
   |
30 |     static ref ALL_BOOKS_BY_SHORT_NAME: HashMap<String, Vec<Book<'static>>> = {
   |                                         ----------------------------------- expected `HashMap<String, Vec<Book<'static>>>` because of return type
...
42 |         map
   |         ^^^ expected struct `Book`, found `&Book<'_>`
   |
   = note: expected struct `HashMap<_, Vec<Book<'static>>>`
              found struct `HashMap<_, Vec<&Book<'_>>>`

What does the '_ mean here? How do we solve that?

3
  • 2
    play.rust-lang.org/… Commented Sep 18, 2022 at 13:40
  • @Stargateur Looks great! Would you mind posting that as an answer as well? Commented Sep 18, 2022 at 14:41
  • 1
    it's just a variation of chayim's answer Commented Sep 18, 2022 at 23:30

1 Answer 1

3

The '_ is "some lifetime". The actual lifetime is 'static, but the compiler doesn't know that at this stage of the compilation.

The problem is that you're iterating over a slice of Book<'static>s. And inserting items to the map. But iteration over a slice produces references, while your maps is expecting owned Books.

The fix is simple: clone or copy the books.

#[derive(Clone, Copy)]
struct Book<'a> { ... }

lazy_static! {
    static ref ALL_BOOKS_BY_SHORT_NAME: HashMap<String, Vec<Book<'static>>> = {
        let mut map = HashMap::new();

        for book in ALL_BOOKS {
            if !map.contains_key(book.name) {
                map.insert(book.name.to_owned(), vec![*book]);
            } else {
                let mut list = map.get(book.name).unwrap();
                list.push(*book)
            }
        }

        map
    };
}

Some more notes:

use once_cell::sync::Lazy;

static ALL_BOOKS_BY_SHORT_NAME: Lazy<HashMap<String, Vec<Book<'static>>>> = Lazy::new(|| {
    let mut map = HashMap::<_, Vec<_>>::new();

    for book in ALL_BOOKS {
        map.entry(book.name.to_owned()).or_default().push(*book);
    }

    map
});

This allocates a string even for duplicate keys but given that this is only an initialization routine this likely doesn't matter.

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

4 Comments

"This allocates a string even for duplicate keys but given that this is only an initialization routine this likely doesn't matter." well it's matter for memory footprint
@Stargateur The string is dropped immediately.
I'm talking about the key. name is present in book and the key. (Also on this use case name is static so you should use &static str
@Stargateur Yes, for duplicate keys the string is only allocated for the lookup then freed.

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.