Reputation: 33036
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?
Upvotes: 0
Views: 3367
Reputation: 70850
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 Book
s.
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:
once_cell
to lazy_static
(its API is going to be part of std).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.
Upvotes: 3