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);