具有HashMap查找的迭代器映射。没有任何密钥失败

时间:2016-09-27 06:44:26

标签: hashmap iterator rust

我有String冒号分隔的值。每个子字符串应该是地图中的键。我想编写一个函数,将String转换为Vec个映射值,如果没有任何键,则会失败。

到目前为止,我的尝试还不完整,但我只是迈出了一小步。第一步是将字符串转换为可选u32 s的向量。 (这可能不是最好的方法):

fn parse(string: &String, lookup: HashMap<String, u32, FastHasher>) -> Vec<Option<u32>> {
    string.split(":").map(|s: &str| lookup.get(s)).collect()
}

这导致

error: the trait bound `std::vec::Vec<std::option::Option<u32>>: std::iter::FromIterator<std::option::Option<&u32>>` is not satisfied [--explain E0277]
 --> <anon>:8:52
  |>
8 |>     string.split(":").map(|s: &str| lookup.get(s)).collect()
  |>                                                    ^^^^^^^
note: a collection of type `std::vec::Vec<std::option::Option<u32>>` cannot be built from an iterator over elements of type `std::option::Option<&u32>`

我相信这意味着我需要为Option<&u32>导入或编写自己的from-iterator行为,对吗?

在我完成之后,我如何根据Ok的存在来换行ErrNone

3 个答案:

答案 0 :(得分:6)

HashMap::get()方法将可选的引用返回到地图内的值。所以你有一个超过Option<&u32>的迭代器,但你希望有一个超过Option<u32>的迭代器。这是通过说:

来完成的
lookup.get(s).cloned()
//           ^^^^^^^^^

这使您当前的代码编译。

要回答标题中的问题:有一个整洁的小FromIterator impl:

impl<A, V> FromIterator<Option<A>> for Option<V> where V: FromIterator<A>

这意味着,例如,您可以将类型为Option<u32>的项目的迭代器收集到Option<Vec<u32>>中。这正是你想要的!所以只需更改您的退货类型:

fn parse(string: &str, lookup: &HashMap<String, u32, FastHasher>) -> Option<Vec<u32>> {
//                                                                   ^^^^^^^^^^^^^^^^
    string.split(":").map(|s| lookup.get(s).cloned()).collect()
}

您可以尝试使用代码here on playground

另请注意以下与我提出的与问题无关的更改:

  • string参数现在是&str而不是&String。实际上没有理由通过&String代替&str,因此后者更适合更通用。
  • 不需要闭包参数的显式类型注释。
  • 您可能希望传递对HashMap的引用,因为您的函数不需要拥有它。

答案 1 :(得分:3)

为了避免此问题,您可以返回Vec<Option<&u32>>而不是Vec<Option<u32>>

fn parse(string: &String, lookup: HashMap<String, u32>) -> Vec<Option<u32>> {
    string.split(":").map(|s: &str| if let Some(&e) = lookup.get(s) { Some(e) } else { None }).collect()
}

Matthieu建议将其简化为:

string.split(":").map(|s: &str| lookup.get(s).cloned()).collect()

如果你将它包装在Result中,我不确定它是否会增加价值;之后你可以轻松检查None

let v = vec![Some(1), None, Some(3)];
println!("{:?}", v.contains(&None));

答案 2 :(得分:1)

如果您确实想要返回引用,则应明确指定生命周期:

pub fn parse<'a, 'b>(string: &'a str, lookup: &'b HashMap<String, u32>) -> Vec<Option<&'b u32>> {
    string.split(":").map(|s| lookup.get(s)).collect()
}

关于转换为Result类型的问题的第二部分:

  

在我完成之后,如何根据OkErr取决于   存在任何None s?

这可以通过将结果折叠并累积到Ok<Vec<u32>>来完成。以下示例说明了这个想法。请参阅Rust Playground runnable example。

use std::collections::HashMap;
use std::result::Result;

#[derive(Debug)]
pub struct ParseError {
    key: String,
}

impl ParseError {
    fn new(k: &str) -> ParseError {
        ParseError { key: k.to_owned() }
    }
}

fn parse(string: &str, lookup: &HashMap<String, u32>) -> Result<Vec<u32>, ParseError> {
    string.split(":")
        .fold(Ok(Vec::new()), |res, s| {
            let mut vec = try!(res);
            match lookup.get(s) {
                Some(&v) => {
                    vec.push(v);
                    Ok(vec)
                }
                None => Err(ParseError::new(s)),
            }
        })
}