我有一个结构EnclosingObject
,其中包含一个元组Vec
字段。我想以一种可以从具有以下结构的字符串解析EnclosingObject
的方式实现此结构的FromStr
:<number of tuples> <tuple1 str1> <tuple1 str2> <tuple1 i32> <tuple2 str1> <tuple2 str2>
...
这就是我到目前为止提出的(忽略元组数量无效的情况):
use std::str::FromStr;
use std::num::ParseIntError;
#[derive(Debug)]
struct EnclosingObject{
tuples: Vec<(String, String, i32)>,
}
impl FromStr for EnclosingObject {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let elems_vec = s.split_whitespace().collect::<Vec<_>>();
let mut elems = elems_vec.as_slice();
let num_tuples = elems[0].parse::<usize>()?;
elems = &elems[1..];
let mut tuples = Vec::with_capacity(num_tuples);
for chunk in elems.chunks(3).take(num_tuples){
tuples.push((chunk[0].into(),
chunk[1].into(),
chunk[2].parse::<i32>()?));
}
Ok(EnclosingObject{
tuples : tuples
})
}
}
fn main(){
println!("{:?}", EnclosingObject::from_str("3 a b 42 c d 32 e f 50"));
}
(游乐场)
正如预期的那样,对于一个有效的字符串,它会打印出来:
Ok(EnclosingObject { tuples: [("a", "b", 42), ("c", "d", 32), ("e", "f", 50)] })
对于无效的字符串,例如"3 a b x c d 32 e f 50":
Err(ParseIntError { kind: InvalidDigit })
我能否以更优雅/惯用的方式解析这Vec
元组,例如使用迭代器?
我尝试了map
和collect
的组合,但问题是错误处理:
let tuples = elems
.chunks(3)
.take(num_tuples)
.map(|chunk| (chunk[0].into(),
chunk[1].into(),
chunk[2].parse::<i32>()?))
.collect();
问号运算符似乎在此上下文中不起作用(在元组中)。所以我稍微改变了它:
let tuples = try!(elems
.chunks(3)
.take(num_tuples)
.map(|chunk| {
let integer = chunk[2].parse::<i32>()?;
Ok((chunk[0].into(),
chunk[1].into(),
integer))})
.collect());
。这有效,但再次显得有点麻烦。
问号运算符似乎在此上下文中不起作用(在元组中)。
问题是,?
在失败时返回Err
,而在成功的情况下,您没有返回Ok
。如果您这样做,运算符就可以正常工作。除此之外,您可以通过对迭代器进行操作来避免Vec
的无关分配,使其在空格上拆分:
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut elems = s.split_whitespace();
let num_tuples = elems.next().expect("error handling: count missing").parse()?;
let tuples: Vec<_> = elems
.by_ref()
.tuples()
.map(|(a, b, c)| Ok((a.into(), b.into(), c.parse()?)))
.take(num_tuples)
.collect::<Result<_, _>>()?;
if tuples.len() != num_tuples { panic!("error handling: too few") }
if elems.next().is_some() { panic!("error handling: too many") }
Ok(EnclosingObject { tuples })
}
我还使用了Itertools的tuples
方法,该方法会自动将迭代器分组为元组并收集到Result<Vec<_>, _>
中。我减少了结构中的冗余tuples: tuples
,并为其余的错误处理添加了一些占位符。我删除了Vec::with_capacity
,因为我相信take
设置的size_hint
就足够好了。如果您不信任它,您仍然可以使用 with_capacity
,然后使用迭代器extend
向量。