Макрос карты хэша отказывается от проверки типа, не получая ошибочное (и, казалось бы, ошибочное) сообщение об ошибке? - PullRequest
0 голосов
/ 30 августа 2018

Здесь у меня есть два связанных макроса:

#[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),*])
        }
    };
}

Я в основном использую его в тестах, например:

assert!(foo.one == hash_map!{});
assert!(foo.two == hash_set![String::from("some value")]);

Но по какой-то причине я получаю очень вводящее в заблуждение и бессмысленное сообщение об ошибке:

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, _>`

Я пытался сделать кортеж самой ссылкой для первой, но это не помогло.

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`

Моя цель состоит в том, чтобы попытаться зарезервировать хеш-карту и добавить все в одном пакете, но я просто не могу заставить ее работать правильно. Обратите внимание, что создаваемая мной хэш-карта / набор имеет свои значения.

1 Ответ

0 голосов
/ 30 августа 2018

Минимальное воспроизведение:

fn main() {
    use std::collections::HashMap;
    use std::iter::FromIterator;

    let _ = HashMap::from_iter([("key", "value")].iter());
}

Вы должны перебирать принадлежащие кортежи, а не заимствованные, отсюда и странное сообщение об ошибке:

fn main() {
    use std::collections::HashMap;
    use std::iter::FromIterator;

    let _: HashMap<_, _> = HashMap::from_iter([("key", "value")].iter().cloned());
}

Вы также можете использовать collect, как сказано в документации FromIterator:

FromIterator from_iter редко вызывается явно и вместо этого используется в методе Iterator collect. См. collect документацию для большего количества примеров.

fn main() {
    use std::collections::HashMap;

    let _: HashMap<_, _> = [("key", "value")].iter().cloned().collect();
    // or
    let _: HashMap<_, _> = vec![("key", "value")].into_iter().collect();
}

Обратите внимание, что сообщение об ошибке более понятно при 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<_, _>`

Последняя строка более полезна: итератор должен выполнять итерации по кортежам, а не по ссылкам на кортежи:

impl<K, V, S> FromIterator<(K, V)> for HashMap<K, V, S> 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...