从vec pop中删除unwrap

时间:2017-01-17 10:38:31

标签: error-handling rust

这是一个产生事物向量的函数(可以工作)但是在那里有一些丑陋的展开,因为它涉及弹出另一个向量。

fn load_into_vec(file_buf: String) -> Vec<Data> {

    let mut data_vec: Vec<Data> = Vec::new();

    for line_iter in file_buf.lines() {

        let mut line_vec: Vec<&str> = line_iter.split(' ').collect();

        let (t1, t2, t3, t4): (u32, u32, u32, u32) =
        (
            /** ANOTHER WAY TO WRITE THIS? **/
            line_vec.pop().unwrap().trim().parse::<u32>().ok().unwrap(),
            line_vec.pop().unwrap().trim().parse::<u32>().ok().unwrap(),
            line_vec.pop().unwrap().trim().parse::<u32>().ok().unwrap(),
            line_vec.pop().unwrap().trim().parse::<u32>().ok().unwrap()
        );

        let mut data_node = Data::new();
        data_node.load((t4, t3, t2, t1));
        data_vec.push(data_node);
    }

    data_vec
}

是否有另一种方法可以在不使用unwrap的情况下重写上述块,或者使用unwrap以便在遇到None时不会出现恐慌?

1 个答案:

答案 0 :(得分:2)

您可以使用迭代器和match来编写循环体的更惯用版本。您可以在调用Vec四次时匹配,而不是收集到中间iter.next(),以提取您的四个整数。如果其中一个iter.next()来电没有成功,您就会感到恐慌。

    let mut iter = line_iter.split(' ')
        .map(str::trim)
        .map(str::parse::<u32>)
        .map(|s| s.expect("could not parse as u32"))
        .fuse();

    let tup = match (iter.next(), iter.next(), iter.next(), iter.next()) {
        (Some(t1), Some(t2), Some(t3), Some(t4)) => (t1, t2, t3, t4),
        _ => panic!("line did not contain at least four numbers"),
    };

    let mut data_node = Data::new();
    data_node.load(tup);

我甚至会改写整个函数:

file_buf.lines()
    .map(|line_iter| {
        let mut iter = line_iter.split(' ')
            .map(str::trim)
            .map(str::parse::<u32>)
            .map(|s| s.expect("could not parse as u32"))
            .fuse();

        let tup = match (iter.next(), iter.next(), iter.next(), iter.next()) {
            (Some(t1), Some(t2), Some(t3), Some(t4)) => (t1, t2, t3, t4),
            _ => panic!("line did not contain at least four numbers"),
        };

        let mut data_node = Data::new();
        data_node.load(tup);
        data_node
    })
    .collect()

更好的方法是让函数返回Result,表示发生错误的时间:

enum MyError {
    NotAnInt,
    TooFewNumbers,
    TooManyNumbers,
}

fn load_into_vec2(file_buf: String) -> Result<Vec<Data>, MyError> {
    file_buf.lines()
        .map(|line_iter| {
            let mut iter = line_iter.split(' ')
                .map(str::trim)
                .map(str::parse::<u32>)
                .fuse();

            match (iter.next(), iter.next(), iter.next(), iter.next()) {
                (Some(Ok(t1)), Some(Ok(t2)), Some(Ok(t3)), Some(Ok(t4))) => if iter.next().is_some() {
                    Err(MyError::TooManyNumbers)
                } else {
                    let mut data_node = Data::new();
                    data_node.load((t1, t2, t3, t4));
                    Ok(data_node)
                },
                (None, _, _, _) |
                (_, None, _, _) |
                (_, _, None, _) |
                (_, _, _, None) => Err(MyError::TooFewNumbers),
                (Some(Err(_)), _, _, _) |
                (_, Some(Err(_)), _, _) |
                (_, _, Some(Err(_)), _) |
                (_, _, _, Some(Err(_))) => Err(MyError::NotAnInt),
            }
        })
        .collect()
}