我写了一个方法:
fn foo(input: HashMap<String, Vec<String>>) {...}
然后我意识到,为了编写测试,我想控制迭代顺序(可能是BTreeMap
或LinkedHashMap
)。这导致了两个问题:
HashMap
的文档中没有看到任何有希望的东西。事实证明,在这种方法中,我只想迭代地图条目,然后迭代每个字符串向量中的项目,但无法找出用于指定此项的正确语法。写这个的正确方法是什么?
fn foo(input: IntoIterator<(String, IntoIterator<String>)>) {...}
答案 0 :(得分:1)
没有特征为容器定义公共接口。可能适合您的唯一特征是Index
特征。
请参阅下文,了解IntoIterator
和Index
特征的正确语法的工作示例。如果您不想使用输入,则需要使用引用,因此请注意生命周期参数。
use std::ops::Index;
use std::iter::IntoIterator;
use std::collections::HashMap;
// this consume the input
fn foo<I: IntoIterator<Item = (String, String)>>(input: I) {
let mut c = 0;
for _ in input {
c += 1;
}
println!("{}", c);
}
// maybe you want this
fn foo_ref<'a, I: IntoIterator<Item = (&'a String, &'a String)>>(input: I) {
let mut c = 0;
for _ in input {
c += 1;
}
println!("{}", c);
}
fn get<'a, I: Index<&'a String, Output = String>>(table: &I, k: &'a String) {
println!("{}", table[k]);
}
fn main() {
let mut h = HashMap::<String, String>::new();
h.insert("one".to_owned(), "1".to_owned());
h.insert("two".to_owned(), "2".to_owned());
h.insert("three".to_owned(), "3".to_owned());
foo_ref(&h);
get(&h, &"two".to_owned());
}
修改强>
我将值类型更改为实现IntoIterator
特征的所有内容:
use std::ops::Index;
use std::iter::IntoIterator;
use std::collections::HashMap;
use std::collections::LinkedList;
fn foo_ref<'a, B, I, >(input: I)
where B : IntoIterator<Item = String>, I: IntoIterator<Item = (&'a String, &'a B)> {
//
}
fn get<'a, B, I>(table: &I, k: &'a String)
where B : IntoIterator<Item = String>, I: Index<&'a String, Output = B>
{
// do something with table[k];
}
fn main() {
let mut h1 = HashMap::<String, Vec<String>>::new();
let mut h2 = HashMap::<String, LinkedList<String>>::new();
foo_ref(&h1);
get(&h1, &"two".to_owned());
foo_ref(&h2);
get(&h2, &"two".to_owned());
}
答案 1 :(得分:1)
描述抽象的HashMap没有这样的特性。我相信没有计划制作一个。到目前为止,最好的答案是你的#2建议:对于一个只读的HashMap,你可能只是想要迭代一些东西。
要在语法级别回答,您尝试编写:
fn foo(input: IntoIterator<(String, IntoIterator<String>)>)
但这是无效的,因为IntoIterator
没有模板参数:
pub trait IntoIterator where Self::IntoIter::Item == Self::Item {
type Item;
type IntoIter: Iterator;
fn into_iter(self) -> Self::IntoIter;
}
它需要两个关联类型,所以你真正想表达的可能是以下内容(在内部我将嵌套的IntoIterator
更改为具体类型,如Vec
为简单起见:
fn foo<I>(input: I)
where I: IntoIterator<
Item=(String, Vec<String>),
IntoIter=IntoIter<String, Vec<String>>>
然而,IntoIterator
的选择并不总是合适的,因为它意味着所有权的转移。如果您只是为了只读目的而借用HashMap,那么使用HashMap的标准迭代器特性Iterator<Item=(&'a String, &'a Vec<String>)>
可能会更好。
fn foo_iter<'a, I>(input: I)
where I: Iterator<Item=(&'a String, &'a Vec<String>)>
与第一个版本不同,您可以通过请求新迭代器多次使用它。
let mut h = HashMap::new();
h.insert("The Beatles".to_string(),
vec!["Come Together".to_string(),
"Twist And Shout".to_string()]);
h.insert("The Rolling Stones".to_string(),
vec!["Paint It Black".to_string(),
"Satisfaction".to_string()]);
foo_iter(h.iter());
foo_iter(h.iter());
foo(h);
//foo(h); <-- error: use of moved value: `h`
正如评论中所述,以下是嵌套foo
的{{1}}版本,而不是更简单的IntoIterators
:
Vec