簡體   English   中英

一般地迭代地圖或2元組的向量

[英]Iterating generically over either a map or a vector of 2-tuples

由於原因 ,我想定義一個泛型函數,它可以迭代表示為映射的鍵值對,或者作為2元組的向量(或滿足IntoIterator<Item=(K, V)>任何其他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);
}

我有一個適用於HashMap的operate定義,但不適用於矢量:

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的錯誤?

IntoIterator提供的功能消耗自我。

fn into_iter(self) -> Self::IntoIter

為了允許在不使用集合的情況下使用IntoIteratorVecHashMap分別實現了對於&'a Vec<T>&'a HashMap<K,V,S>IntoIterator 但是,它們並不完全相同。

對於哈希映射,每個Item都是(&K, &V) ,它不會產生問題,因為代碼有效地將項目視為2個大小的鍵元組和強制轉換為&str值。 並且&&str確實強制到&str 對於向量,每個Item都是&T (因此在這種情況下為&(K, V) ),但由於函數期望(K, V)作為迭代項,因此它目前無法處理&(K, V)

實際上,如果移動向量,該函數將起作用,從而產生一個其中Item = (K, V)IntoIterator

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)));

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM