Specific HashMap type
kurikomoe opened this issue · 1 comments
TL;DR
This issue suggests the crate providing a new hashmap_ex!
macro which allows user to specify the HashMap<K, V>
type.
let map = hashmap_ex!(
HashMap<_, i32>,
{
"One", 1,
"Two", 2,
}
)
// expaned as
let map = {
let _cap = <[()]>::len(&[(), ()]);
let mut _map: HashMap<_, i32> = ::std::collections::HashMap::with_capacity(_cap);
let _ = _map.insert("One", 1);
let _ = _map.insert("Two", 2);
_map
};
Full:
Recently I came cross a problem that hashmap!
cannot be used to init a HashMap<_, Box<dyn Trait>>
object.
#[macro_use] extern crate maplit;
use std::collections::HashMap;
struct Foo;
struct Bar;
trait Zoo {}
impl Zoo for Foo {}
impl Zoo for Bar {}
fn main() {
let mm: HashMap<_, Box<dyn Zoo>> = hashmap!(
"Good" => Box::new(Foo {}),
"Bad" => Box::new(Bar {}),
);
}
The compiler complains that
error[E0308]: mismatched types
--> src/main.rs:15:28
|
15 | "Bad" => Box::new(Bar {}),
| -------- ^^^^^^ expected struct `Foo`, found struct `Bar`
| |
| arguments to this function are incorrect
|
.........
|
199 | pub fn new(x: T) -> Self {
| ^^^
error[E0308]: mismatched types
--> src/main.rs:13:40
|
13 | let mm: HashMap<_, Box<dyn Zoo>> = hashmap!(
| ________________________________________^
14 | | "Good" => Box::new(Foo {}),
15 | | "Bad" => Box::new(Bar {}),
16 | | );
| |_____^ expected trait object `dyn Zoo`, found struct `Foo`
|
= note: expected struct `HashMap<_, Box<dyn Zoo>>`
found struct `HashMap<&str, Box<Foo>>`
after using cargo expand
to examine the generated code, I found out that the hashmap!
strongly depend on the compiler's type infer mechanics.
#[macro_use]
extern crate maplit;
use std::collections::HashMap;
struct Foo;
struct Bar;
trait Zoo {}
impl Zoo for Foo {}
impl Zoo for Bar {}
fn main() {
let mm: HashMap<_, Box<dyn Zoo>> = {
let _cap = <[()]>::len(&[(), ()]);
// ERROR: At this time, the compiler cannot know the exact type of the HashMap.
let mut _map = ::std::collections::HashMap::with_capacity(_cap);
let _ = _map.insert("Good", Box::new(Foo {}));
let _ = _map.insert("Bad", Box::new(Bar {}));
_map
};
}
To avoid this, current hashmap!
macro needs users to write the following code:
let mm = hashmap!(
"Good" => Box::new(Foo {}) as Box<dyn Zoo>,
"Bad" => Box::new(Bar {}) as Box<dyn Zoo>,
);
So, I suggest a new macro hashmap_ex!
which add a $t:ty
in its parameters, which you can see in the patch that comes along with this issue.
let map = hashmap_ex!(
HashMap<_, i32>,
{
"One", 1,
"Two", 2,
}
)
Possible Problems
- The
{}
is required for the data, aka, you cannot use()
nor[]
to wrap the data.
hashmap_ex!(
HashMap<_, _>,
{ // <--- the brace is forced to be "{}"
"One" => 1,
"Two" => 2,
}
)
- The
convert_args
is not working forhashmap_ex!
Also useful if you need HashMaps with a specific Hasher, for testing I use a deterministic Hasher which has a different type than the standard HashMap:
hashmap_ex!(
HashMap<_, _, BuildHasherDefault<DefaultHasher>>,
{
...
}
)
DefaultHasher
BuildHasherDefault
(standard HashMap has type: HashMap<_, _, RandomState>
)