一般地迭代地图或2元组的向量

zwo*_*wol 3 iterator traits rust

由于原因,我想定义一个泛型函数,它可以迭代键值对,表示为映射,或者是2元组的向量(或任何其他满足IntoIterator<Item=(K, V)>,其中KV是字符串的).具体来说,我想要这个工作:

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);
}
Run Code Online (Sandbox Code Playgroud)

我有一个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());
    }
}
Run Code Online (Sandbox Code Playgroud)

我得到的错误信息是

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`
Run Code Online (Sandbox Code Playgroud)

而且我根本不明白.一方面,它似乎是倒退,另一方面,为什么我只得到错误Vec而不是HashMap

E_n*_*ate 6

IntoIterator消耗自己提供的功能.

fn into_iter(self) -> Self::IntoIter
Run Code Online (Sandbox Code Playgroud)

为了允许使用IntoIterator不消耗收集,双方VecHashMap有实现IntoIterator&'a Vec<T>&'a HashMap<K,V,S>分别.但是,它们并不完全相同.

对于哈希映射,每个Item都是a (&K, &V),这不会产生问题,因为代码有效地将项目视为2个大小的密钥和强制值的元组&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);
Run Code Online (Sandbox Code Playgroud)

但是,如果我们希望它能够在不消耗任何产品的情况下为两个系列工作呢?好吧,我刚刚设计了两个解决方案.

#1

这个涉及将元组隐藏在一个新的特征背后:

/// for stuff that can be turned into a pair of references
trait AsRefPair<K, V> {
    fn as_ref_pair(&self) -> (&K, &V);
}
Run Code Online (Sandbox Code Playgroud)

实现它为&(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)
    }
}
Run Code Online (Sandbox Code Playgroud)

现在这个功能有效:

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());
    }
}
Run Code Online (Sandbox Code Playgroud)

游乐场.起初听起来有点疯狂,但是......!

#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());
    }
}
Run Code Online (Sandbox Code Playgroud)

游乐场.我发现这个更惯用.