Reputation: 1269
I've got these two related macros here:
#[macro_export]
macro_rules! hash_map {
( $( $key:expr => $value:expr ),* ) => {
{
use ::std::iter::FromIterator;
::std::collections::HashMap::from_iter(&[$(($key, $value)),*])
}
};
}
#[macro_export]
macro_rules! hash_set {
( $( $x:expr ),* ) => {
{
use ::std::iter::FromIterator;
::std::collections::HashSet::from_iter(&[$($x),*])
}
};
}
I'm primarily using it in tests, like this:
assert!(foo.one == hash_map!{});
assert!(foo.two == hash_set![String::from("some value")]);
But for some reason, I'm getting a very misleading and nonsensical error message out of it:
error[E0271]: type mismatch resolving `<&[_; 0] as std::iter::IntoIterator>::Item == (_, _)`
--> src/common.rs:M:N
|
6 | ::std::collections::HashMap::from_iter(&[$(($key, $value)),*])
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected reference, found tuple
|
::: src/foo.rs:M:N
|
154 | assert!(foo.one == hash_map!{});
| ----------- in this macro invocation
|
= note: expected type `&_`
found type `(_, _)`
= note: required by `std::iter::FromIterator::from_iter`
error[E0308]: mismatched types
--> src/common.rs:M:N
|
16 | ::std::collections::HashSet::from_iter(&[$($x),*])
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::string::String`, found reference
|
::: src/foo.rs:M:N
|
150 | fn it_can_do_things() {
| - help: try adding a return type: `-> std::collections::HashSet<&std::string::String, _>`...
155 | assert!(foo.two == hash_set![String::from("some value")]);
| -------------------------------------------- in this macro invocation
|
= note: expected type `std::collections::HashSet<std::string::String, std::collections::hash_map::RandomState>`
found type `std::collections::HashSet<std::string::String, std::collections::hash_map::RandomState>`
found type `std::collections::HashSet<&std::string::String, _>`
I've tried making the tuple a reference itself for the first, but that didn't help.
error[E0271]: type mismatch resolving `<&[_; 0] as std::iter::IntoIterator>::Item == (_, _)`
--> src/common.rs:M:N
|
6 | ::std::collections::HashMap::from_iter(&[$(&($key, $value)),*])
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected reference, found tuple
|
::: src/foo.rs:M:N
|
154 | assert!(foo.one == hash_map!{});
| ----------- in this macro invocation
|
= note: expected type `&_`
found type `(_, _)`
= note: required by `std::iter::FromIterator::from_iter`
My goal is to try to get the hash map to reserve and add everything in a single batch, but I just can't get it to work correctly. Note that the hash map/set I'm creating owns its values.
Upvotes: 2
Views: 546
Reputation: 42849
Minimal reproduction:
fn main() {
use std::collections::HashMap;
use std::iter::FromIterator;
let _ = HashMap::from_iter([("key", "value")].iter());
}
You must iterate over owned tuples, not borrowed, hence the strange error message:
fn main() {
use std::collections::HashMap;
use std::iter::FromIterator;
let _: HashMap<_, _> = HashMap::from_iter([("key", "value")].iter().cloned());
}
You can also use collect
as said in the documentation of FromIterator
:
FromIterator
'sfrom_iter
is rarely called explicitly, and is instead used throughIterator
'scollect
method. Seecollect
's documentation for more examples.
fn main() {
use std::collections::HashMap;
let _: HashMap<_, _> = [("key", "value")].iter().cloned().collect();
// or
let _: HashMap<_, _> = vec![("key", "value")].into_iter().collect();
}
Note that the error message is more understandable with collect
:
error[E0277]: the trait bound `std::collections::HashMap<_, _>: std::iter::FromIterator<&(&str, &str)>` is not satisfied
--> src/main.rs:4:54
|
4 | let _: HashMap<_, _> = [("key", "value")].iter().collect();
| ^^^^^^^ a collection of type `std::collections::HashMap<_, _>` cannot be built from an iterator over elements of type `&(&str, &str)`
|
= help: the trait `std::iter::FromIterator<&(&str, &str)>` is not implemented for `std::collections::HashMap<_, _>`
The last line is the more helpful: the iterator must iterate over tuples and not references to tuples:
impl<K, V, S> FromIterator<(K, V)> for HashMap<K, V, S>
Upvotes: 5