如何使用带有Result返回值的fold?

时间:2017-01-10 13:01:19

标签: error-handling functional-programming rust

我有一个功能:

fn test<T: FromStr>(text: &str) -> Result<T, SaleError>
在这种情况下,

SaleError是我的自定义错误结构。

我有2个数组,我想将相同的位置值相乘并对结果求和:

fn calculate_result() -> Result<f64, SaleError> {
    let q_coms = ["1", "2", "3"];
    let v_un_coms = ["1", "2", "3"];
    Ok(try!(q_coms.iter().zip(v_un_coms.iter()).fold(0.0, mult_and_sum)))
}

该函数尝试压缩两个数组,然后尝试在fold函数上调用mult_and_sum

fn mult_and_sum(sum: f64, (q_com, v_un_com): (&str, &str)) -> Result<f64, SaleError> {
    Ok(sum + try!(test::<f64>(q_com)) * try!(test::<f64>(v_un_com)))
}

据我所知,问题是fold期望带有签名的函数

fn mult_and_sum(sum: f64, (q_com, v_un_com): (&str, &str)) -> f64

由于test功能可能失败,如何将foldResult返回值一起使用,如果失败,calculate_result将返回Err(SaleError)

2 个答案:

答案 0 :(得分:5)

没有必要猜测,错误消息告诉你。我重组了它以使其更加明显:

actual:   for<'r, 'r> std::ops::FnMut<(f64, (&'r str, &'r str))>
required:             std::ops::FnMut<({float}, (&&str, &&str))>

它还说(expected &str, found str)。这是因为你有一片字符串切片(&[&str])。切片的迭代器返回对每个元素的引用,因此值为&&str类型。该函数接受&str,因此不匹配。

另一方面,documentation for fold显示了它的期望:

fn fold<B, F>(self, init: B, f: F) -> B 
    where F: FnMut(B, Self::Item) -> B

单词:fold按值(self)获取迭代器,某种类型的初始值(init: B)和函数(f: F)。该函数采用该类型,迭代器值,并返回类型(FnMut(B, Self::Item) -> B)。

向后工作,您想要返回Result,因此必须修复B。这意味着你的累加器必须是Result,大概你想要一个Ok,否则我们将从一开始就失败了。让我们直接实现它:

fn calculate_result() -> Result<f64, SaleError> {
    let q_coms = ["1", "2", "3"];
    let v_un_coms = ["1", "2", "3"];
    let pairs = q_coms.iter().zip(v_un_coms.iter());
    pairs.fold(Ok(0.0), |acc, (a, b)| {
        match acc {
            Ok(old) => mult_and_sum(old, (a, b)),
            other => other,
        }
    })
}

可以使用and_then

进行简化
pairs.fold(Ok(0.0), |acc, (a, b)| 
    acc.and_then(|old| mult_and_sum(old, (a, b)))
)

然后可能小一点:

let pairs = q_coms.iter().cloned().zip(v_un_coms.iter().cloned());
pairs.fold(Ok(0.0), |acc, i| acc.and_then(|old| mult_and_sum(old, i)))

我们使用cloned&&str转换为&str,但您也可以将功能更改为接受&&str

答案 1 :(得分:2)

  

由于test函数可能失败,如何使用带有Result返回值的fold,如果失败,[如果一个元素是Err]?

你的累加器(在每次迭代中改变&#34;&#34;)也需要Result!看看这段代码:

let q_coms = ["1", "2", "3"];
let v_un_coms = ["1", "2", "3"];

q_coms.iter()
    // We don't need to call `iter()` on `v_un_coms`, because `zip()`
    // takes an argument which implements `IntoIterator`
    .zip(&v_un_coms)

    // As you can see: the starting value is `Ok(0.0)` to say: so far, 
    // there was no error.
    .fold(Ok(0.0), |acc, (a, b)| {
        // The `and_then()` method calls the given closure when the `acc`
        // (the outer one) is `Ok`. The inner `acc` represents the `Ok` 
        // value. The closure will then return another `Result`.
        acc.and_then(|acc| {
            // More fun with `and_then()` and `map()`. Read docs for more
            // information.
            test::<f64>(a)
                .and_then(|a| test::<f64>(b).map(|b| a * b))
                .map(|new_product| acc + new_product)
        })
    })

Try it on playground