zwo*_*wol 3 iterator traits rust
由于原因,我想定义一个泛型函数,它可以迭代键值对,表示为映射,或者是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);
}
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
?
IntoIterator
消耗自己提供的功能.
fn into_iter(self) -> Self::IntoIter
Run Code Online (Sandbox Code Playgroud)
为了允许使用IntoIterator
不消耗收集,双方Vec
并HashMap
有实现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)
但是,如果我们希望它能够在不消耗任何产品的情况下为两个系列工作呢?好吧,我刚刚设计了两个解决方案.
这个涉及将元组隐藏在一个新的特征背后:
/// 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)
游乐场.起初听起来有点疯狂,但是......!
在这一个中,只需停止使用元组...并开始使用键值!
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)
游乐场.我发现这个更惯用.