我有一个功能:
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
功能可能失败,如何将fold
与Result
返回值一起使用,如果失败,calculate_result
将返回Err(SaleError)
?
答案 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)
})
})