习惯性地将空格分隔的字符串解析为不同类型的元组的 Vec



我有一个结构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元组,例如使用迭代器?

我尝试了mapcollect的组合,但问题是错误处理:

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向量。

最新更新