Общая итерация по карте или вектору из двух кортежей
По причинам, я хочу определить универсальную функцию, которая может перебирать пары ключ-значение, выраженные либо как отображение, либо как вектор из 2-х кортежей (или чего-либо еще, что удовлетворяет IntoIterator<Item=(K, V)>
, где K
а также V
стянуты). Конкретно, я хочу, чтобы это работало:
use std::collections::HashMap;
fn main() {
let vc = vec![
("a", "foo"),
("b", "bar"),
("c", "baz")
];
operate(&vc);
let mut map = HashMap::new();
map.insert("d", "blurf");
map.insert("e", "quux");
map.insert("f", "xyzzy");
operate(&map);
}
У меня есть определение operate
это работает для HashMap, но не для вектора:
fn operate<I, K, V>(x: I)
where I: IntoIterator<Item=(K, V)>,
K: AsRef<str>, V: AsRef<str>
{
for (ref k, ref v) in x {
println!("{}: {}", k.as_ref(), v.as_ref());
}
}
Я получаю сообщение об ошибке:
error[E0271]: type mismatch resolving `<&std::vec::Vec<(&str, &str)> as std::iter::IntoIterator>::Item == (_, _)`
--> test.rs:18:5
|
18 | operate(&vc);
| ^^^^^^^ expected reference, found tuple
|
= note: expected type `&(&str, &str)`
= note: found type `(_, _)`
= note: required by `operate`
и я совсем не понимаю С одной стороны, кажется, что это задом наперед, а с другой, почему я получаю только ошибку для Vec
а не HashMap
?
2 ответа
Функция предоставлена IntoIterator
потребляет себя.
fn into_iter(self) -> Self::IntoIter
Для того, чтобы разрешить использование IntoIterator
без потребления коллекции, оба Vec
а также HashMap
есть реализации IntoIterator
за &'a Vec<T>
а также &'a HashMap<K,V,S>
соответственно. Однако они не совсем одинаковы.
Для хэш-карты каждый Item
это (&K, &V)
, что не создает проблемы, потому что код эффективно принимает элементы в виде двухразмерных кортежей ключей и значений, которые приводят к &str
, А также &&str
действительно принуждает к &str
, Для вектора каждый Item
это &T
(таким образом, &(K, V)
в этом случае), а потому что функция ожидает (K, V)
как итерирующий элемент, он в настоящее время не может иметь дело с элементами &(K, V)
,
Как таковая, функция работает, если вы перемещаете вектор, что дает IntoIterator
где Item = (K, V)
:
let vc = vec![
("a", "foo"),
("b", "bar"),
("c", "baz")
];
operate(vc);
Но что, если мы хотим, чтобы он работал для обеих коллекций, не потребляя ни одной из них? Ну, я только что разработал два решения.
# 1
Это включает в себя сокрытие кортежа за новой чертой:
/// for stuff that can be turned into a pair of references
trait AsRefPair<K, V> {
fn as_ref_pair(&self) -> (&K, &V);
}
Реализация его для &(K,V)
а также (&K,&V)
:
impl<'a, K, V> AsRefPair<K, V> for (&'a K, &'a V) {
fn as_ref_pair(&self) -> (&K, &V) {
(self.0, self.1)
}
}
impl<'a, K, V> AsRefPair<K, V> for &'a (K, V) {
fn as_ref_pair(&self) -> (&K, &V) {
(&self.0, &self.1)
}
}
И теперь эта функция работает:
fn operate<I, T, K, V>(x: I)
where I: IntoIterator<Item=T>,
T: AsRefPair<K, V>,
K: AsRef<str>, V: AsRef<str>
{
for p in x {
let (ref k, ref v) = p.as_ref_pair();
println!("{}: {}", k.as_ref(), v.as_ref());
}
}
Детская площадка Поначалу это может показаться немного сумасшедшим, но...!
# 2
В этом просто перестаньте работать с кортежами... и начните работать со значениями ключей!
trait KeyValue<K, V> {
fn key_value(&self) -> (&K, &V) {
(self.key(), self.value())
}
fn key(&self) -> &K;
fn value(&self) -> &V;
}
impl<K, V> KeyValue<K, V> for (K, V) {
fn key(&self) -> &K {
&self.0
}
fn value(&self) -> &V {
&self.1
}
}
impl<'a, K, V> KeyValue<K, V> for &'a (K, V) {
fn key(&self) -> &K {
&self.0
}
fn value(&self) -> &V {
&self.1
}
}
fn operate<I, T, K, V>(x: I)
where I: IntoIterator<Item=T>,
T: KeyValue<K, V>,
K: AsRef<str>, V: AsRef<str>
{
for p in x {
let (ref k, ref v) = p.key_value();
println!("{}: {}", k.as_ref(), v.as_ref());
}
}
Детская площадка Я нахожу это немного более идиоматичным.
Если вы перейдете к функции operate()
итератор вместо ссылки на вектор, вы можете использовать Iterator
адаптеры для преобразования Iterator::Item
к тому, что вам нужно:
operate(vc.iter().map(|&(ref a, ref b)| (a, b)));