英文:
How to propagate `Result` through `.max_by_key()`
问题
// Given a vec of integers `i`, find the one for which `float_or_err(i)` is
// largest. If any of those integers `i` yields an error in `float_or_err(i)`,
// return the error.
fn max_by_float_or_err(v: Vec<isize>) -> Option<&isize> {
// Use the `max_by_key` method with a custom closure to compare `Result` values.
return v.iter().max_by_key(|i| {
match float_or_err(i) {
Ok(result) => result,
Err(_) => OrderedFloat(std::f64::NEG_INFINITY), // Return the smallest value for errors
}
});
}
This code defines the max_by_float_or_err
function, which uses the max_by_key
method with a custom closure to compare the Result
values returned by float_or_err(i)
. If float_or_err(i)
returns an Ok
value, it uses that value for comparison. If it returns an Err
value, it uses OrderedFloat(std::f64::NEG_INFINITY)
as a sentinel value to represent the smallest possible value for errors, ensuring that errors are propagated correctly while finding the maximum value.
英文:
I have a function float_or_err(i)
that takes an integer and returns either a float or an error. I want to write a function that takes an array of integers and returns the one for which float_or_err(i)
is greatest, or propagates the error if float_or_err()
errors on any of the i
s.
Now, I have read Using max_by_key on a vector of floats [duplicate] and I understand why Rust doesn't implement a total ordering on floats, and I have decided to use the ordered_float
crate instead of f64
to impose a total ordering on the results of float_or_err()
.
However, that question does not address the case where the argument to .max_by_key()
is a closure that returns a Result<OrderedFloat<f64>, _>
, as in the following example:
use ordered_float::OrderedFloat;
fn main() {}
enum ErrorKind {
ValidationError
}
// Function of `i` that normally returns an f64 but sometimes has an error
fn float_or_err(i: &isize) -> Result<OrderedFloat<f64>, ErrorKind> {
if i % 5 == 0 {
return Err(ErrorKind::ValidationError);
}
else {
return Ok(OrderedFloat(3.14));
}
}
// Given a vec of integers `i`, find the one for which `float_or_err(i)` is
// largest. If any of those integers `i` yields an error in `float_or_err(i)`,
// return the error.
fn max_by_float_or_err(v: Vec<isize>) -> Option<&isize> {
return v.iter().max_by_key(|i| float_or_err(i));
}
The compiler does not accept the second-to-last line, because
the trait bound `ErrorKind: Ord` is not satisfied
the trait `Ord` is implemented for `Result<T, E>`
OK, looks like I need to do something like
impl Ord for Result<OrderedFloat<f64>, ErrorKind> {
// ...
}
(where I order first by the float, then use the ErrorKind as a tiebreaker.)
But the compiler won't let me implement Ord
for a type that I didn't create:
`Result` is not defined in the current craterustcE0117
main.rs(9, 1): original diagnostic
only traits defined in the current crate can be implemented for types defined outside of the crate
Actually, my first intuition is to write:
fn argmax_by_float_or_err(v: Vec<isize>) -> Option<&isize> {
return v.iter().max_by_key(|i| float_or_err(i)?);
}
But this doesn't compile either, because you can't use the question mark operator inside of a closure. I have read Alternatives for using the question mark operator inside a map function closure, where the suggested solution is to just to move the question mark to the end of the chain, but that solution is particular to the function in question, where the reduce op is .sum()
instead of .max_by_key()
, and .sum()
is implemented natively for Result. This is not the case for .max_by_key()
(and many other reduce ops).
- In the context of the
max_by_float_or_err()
function defined above, how can I propogate errors fromfloat_or_err()
? - I am looking for a solution that uses
.max_by_key()
(or at least another reduce op) instead of rewriting the body as a for loop.
Edit: I also read How do I implement a trait I don't own for a type I don't own?, which suggests something like:
struct MyResult(Result<OrderedFloat<f64>, ErrorKind>);
impl Ord for MyResult {
// ...
}
This solution is unsatisfactory because instances of MyResult
don't seem to inherit all the methods associated with normal Result
s. For example, I cannot write:
let my_result: OrderedFloat<f64> = float_or_err(-1).unwrap_or_default(6.28);
答案1
得分: 3
问题在于您希望在Err
上进行短路操作,而max_by_key
函数无法实现此功能。我可能会使用for循环来解决这个问题,但也可以使用短路方法,例如try_fold
来实现。
pub fn max_by_float_or_err(v: &[isize]) -> Result<Option<&isize>, ErrorKind> {
let Some((first, rest)) = v.split_first() else { return Ok(None) };
let initial = (first, float_or_err(first)?);
let max: Result<&isize, ErrorKind> = rest
.iter()
.try_fold(initial, |(acc, acc_key), item| {
let item_key = float_or_err(item)?;
if acc_key > item_key {
Ok((acc, acc_key))
} else {
Ok((item, item_key))
}
})
.map(|(item, _key)| item);
Some(max).transpose()
}
将来,使用try_reduce
可能会稍微简单一些。
英文:
The issue here is that you are looking to short-circuit on an Err
, which the max_by_key
function is not capable of. I would probably make this in a for loop, but this is possible using a short-circuiting method such as try_fold
.
pub fn max_by_float_or_err(v: &[isize]) -> Result<Option<&isize>, ErrorKind> {
let Some((first, rest)) = v.split_first() else { return Ok(None) };
let initial = (first, float_or_err(first)?);
let max: Result<&isize, ErrorKind> = rest
.iter()
.try_fold(initial, |(acc, acc_key), item| {
let item_key = float_or_err(item)?;
if acc_key > item_key {
Ok((acc, acc_key))
} else {
Ok((item, item_key))
}
})
.map(|(item, _key)| item);
Some(max).transpose()
}
In the future, this would be slightly simpler with try_reduce
答案2
得分: 2
这里是一个传播错误的max_by_key()
通用版本:
fn try_max_by_key<I, F, K, E>(mut iter: I, mut f: F) -> Result<Option<I::Item>, E>
where
I: Iterator,
F: FnMut(&I::Item) -> Result<K, E>,
K: Ord,
{
let Some(mut curr) = iter.next() else { return Ok(None) };
let mut curr_key = f(&curr)?;
for next in iter {
let next_key = f(&next)?;
if next_key > curr_key {
curr = next;
curr_key = next_key;
}
}
Ok(Some(curr))
}
请注意,这是Rust代码的翻译。
英文:
Here's a generic version of max_by_key()
that propagates errors:
fn try_max_by_key<I, F, K, E>(mut iter: I, mut f: F) -> Result<Option<I::Item>, E>
where
I: Iterator,
F: FnMut(&I::Item) -> Result<K, E>,
K: Ord,
{
let Some(mut curr) = iter.next() else { return Ok(None) };
let mut curr_key = f(&curr)?;
for next in iter {
let next_key = f(&next)?;
if next_key > curr_key {
curr = next;
curr_key = next_key;
}
}
Ok(Some(curr))
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论