If you know your data does not contain NaNs, then assert that fact by unwrapping the comparison:
fn example(x: &[f64]) -> Option<f64> {
x.iter()
.cloned()
.min_by(|a, b| a.partial_cmp(b).expect("Tried to compare a NaN"))
}
If your data may have NaNs, you need to handle that case specifically. One solution is to say that all 16,777,214 NaN values are equal to each other and are always greater than or less than other numbers:
use std::cmp::Ordering;
fn example(x: &[f64]) -> Option<f64> {
x.iter()
.cloned()
.min_by(|a, b| {
// all NaNs are greater than regular numbers
match (a.is_nan(), b.is_nan()) {
(true, true) => Ordering::Equal,
(true, false) => Ordering::Greater,
(false, true) => Ordering::Less,
_ => a.partial_cmp(b).unwrap(),
}
})
}
There are numerous crates available that can be used to give you whichever semantics your code needs.
You should not use partial_cmp(b).unwrap_or(Ordering::Equal)
because it provides unstable results when NaNs are present, but it leads the reader into thinking that they are handled:
use std::cmp::Ordering;
use std::f64;
fn example(x: &[f64]) -> Option<f64> {
x.iter()
.cloned()
.min_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal))
}
fn main() {
println!("{:?}", example(&[f64::NAN, 1.0]));
println!("{:?}", example(&[1.0, f64::NAN]));
}
Some(NaN)
Some(1.0)