我有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
的存在来换行Err
或None
?
答案 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
类型的问题的第二部分:
在我完成之后,如何根据
Ok
或Err
取决于 存在任何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)),
}
})
}