通用映射作为函数参数

时间:2015-07-16 07:04:30

标签: dictionary rust

我写了一个方法:

fn foo(input: HashMap<String, Vec<String>>) {...}

然后我意识到,为了编写测试,我想控制迭代顺序(可能是BTreeMapLinkedHashMap)。这导致了两个问题:

  1. 是否有一些我可以使用的特性或特征组合基本上表达了#34;字符串到字符串向量的映射&#34;?我在HashMap的文档中没有看到任何有希望的东西。
  2. 事实证明,在这种方法中,我只想迭代地图条目,然后迭代每个字符串向量中的项目,但无法找出用于指定此项的正确语法。写这个的正确方法是什么?

    fn foo(input: IntoIterator<(String, IntoIterator<String>)>) {...}
    

2 个答案:

答案 0 :(得分:1)

没有特征为容器定义公共接口。可能适合您的唯一特征是Index特征。 请参阅下文,了解IntoIteratorIndex特征的正确语法的工作示例。如果您不想使用输入,则需要使用引用,因此请注意生命周期参数。

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`

Full gist

修改

正如评论中所述,以下是嵌套foo的{​​{1}}版本,而不是更简单的IntoIterators

Vec