Rollup merge of #109409 - WaffleLapkin:progamer, r=dtolnay

Add `minmax{,_by,_by_key}` functions to `core::cmp`

This PR adds the following functions:

```rust
// mod core::cmp
#![unstable(feature = "cmp_minmax")]

pub fn minmax<T>(v1: T, v2: T) -> [T; 2]
where
    T: Ord;

pub fn minmax_by<T, F>(v1: T, v2: T, compare: F) -> [T; 2]
where
    F: FnOnce(&T, &T) -> Ordering;

pub fn minmax_by_key<T, F, K>(v1: T, v2: T, mut f: F) -> [T; 2]
where
    F: FnMut(&T) -> K,
    K: Ord;
```
(they are also `const` under `#[feature(const_cmp)]`, I've omitted `const` stuff for simplicity/readability)

----

Semantically these functions are equivalent to `{ let mut arr = [v1, v2]; arr.sort(); arr }`, but since they operate on 2 elements only, they are implemented as a single comparison.

Even though that's basically a sort, I think "sort 2 elements" operation is useful on it's own in many cases. Namely, it's a common pattern when you have 2 things, and need to know which one is smaller/bigger to operate on them differently.

I've wanted such functions countless times, most recently in #109402, so I thought I'd propose them.

----

r? libs-api
This commit is contained in:
Matthias Krüger 2023-09-18 18:27:18 +02:00 committed by GitHub
commit db9e217989
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1289,6 +1289,91 @@ pub fn max_by_key<T, F: FnMut(&T) -> K, K: Ord>(v1: T, v2: T, mut f: F) -> T {
max_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2)))
}
/// Compares and sorts two values, returning minimum and maximum.
///
/// Returns `[v1, v2]` if the comparison determines them to be equal.
///
/// # Examples
///
/// ```
/// #![feature(cmp_minmax)]
/// use std::cmp;
///
/// assert_eq!(cmp::minmax(1, 2), [1, 2]);
/// assert_eq!(cmp::minmax(2, 2), [2, 2]);
///
/// // You can destructure the result using array patterns
/// let [min, max] = cmp::minmax(42, 17);
/// assert_eq!(min, 17);
/// assert_eq!(max, 42);
/// ```
#[inline]
#[must_use]
#[unstable(feature = "cmp_minmax", issue = "115939")]
pub fn minmax<T>(v1: T, v2: T) -> [T; 2]
where
T: Ord,
{
if v1 <= v2 { [v1, v2] } else { [v2, v1] }
}
/// Returns minimum and maximum values with respect to the specified comparison function.
///
/// Returns `[v1, v2]` if the comparison determines them to be equal.
///
/// # Examples
///
/// ```
/// #![feature(cmp_minmax)]
/// use std::cmp;
///
/// assert_eq!(cmp::minmax_by(-2, 1, |x: &i32, y: &i32| x.abs().cmp(&y.abs())), [1, -2]);
/// assert_eq!(cmp::minmax_by(-2, 2, |x: &i32, y: &i32| x.abs().cmp(&y.abs())), [-2, 2]);
///
/// // You can destructure the result using array patterns
/// let [min, max] = cmp::minmax_by(-42, 17, |x: &i32, y: &i32| x.abs().cmp(&y.abs()));
/// assert_eq!(min, 17);
/// assert_eq!(max, -42);
/// ```
#[inline]
#[must_use]
#[unstable(feature = "cmp_minmax", issue = "115939")]
pub fn minmax_by<T, F>(v1: T, v2: T, compare: F) -> [T; 2]
where
F: FnOnce(&T, &T) -> Ordering,
{
if compare(&v1, &v2).is_le() { [v1, v2] } else { [v2, v1] }
}
/// Returns minimum and maximum values with respect to the specified key function.
///
/// Returns `[v1, v2]` if the comparison determines them to be equal.
///
/// # Examples
///
/// ```
/// #![feature(cmp_minmax)]
/// use std::cmp;
///
/// assert_eq!(cmp::minmax_by_key(-2, 1, |x: &i32| x.abs()), [1, -2]);
/// assert_eq!(cmp::minmax_by_key(-2, 2, |x: &i32| x.abs()), [-2, 2]);
///
/// // You can destructure the result using array patterns
/// let [min, max] = cmp::minmax_by_key(-42, 17, |x: &i32| x.abs());
/// assert_eq!(min, 17);
/// assert_eq!(max, -42);
/// ```
#[inline]
#[must_use]
#[unstable(feature = "cmp_minmax", issue = "115939")]
pub fn minmax_by_key<T, F, K>(v1: T, v2: T, mut f: F) -> [T; 2]
where
F: FnMut(&T) -> K,
K: Ord,
{
minmax_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2)))
}
// Implementation of PartialEq, Eq, PartialOrd and Ord for primitive types
mod impls {
use crate::cmp::Ordering::{self, Equal, Greater, Less};