Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

impl ops::Try for Ordering #76041

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions library/core/src/cmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
#![stable(feature = "rust1", since = "1.0.0")]

use self::Ordering::*;
use crate::convert::TryFrom;
use crate::ops;

/// Trait for equality comparisons which are [partial equivalence
/// relations](https://en.wikipedia.org/wiki/Partial_equivalence_relation).
Expand Down Expand Up @@ -679,6 +681,90 @@ impl PartialOrd for Ordering {
}
}

/// An `UnequalOrdering` is an [`Ordering`] which is either `Less` or `Greater`.
/// It can be obtained from an [`Ordering`] using [`TryFrom`] or [`TryInto`].
/// It can be converted back to an [`Ordering`] using [`From`] or [`Into`].
///
/// # Examples
///
/// ```
/// #![feature(try_trait)]
///
/// use std::cmp::{Ordering, UnequalOrdering};
/// use std::convert::{TryFrom, TryInto};
///
/// assert_eq!(Ordering::Less.try_into(), Ok(UnequalOrdering::Less));
/// assert_eq!(Ordering::Greater.try_into(), Ok(UnequalOrdering::Greater));
/// assert!(UnequalOrdering::try_from(Ordering::Equal).is_err());
///
/// assert_eq!(Ordering::from(UnequalOrdering::Less), Ordering::Less);
/// assert_eq!(Ordering::from(UnequalOrdering::Greater), Ordering::Greater);
/// ```
///
/// [`TryFrom`]: crate::convert::TryFrom
/// [`TryInto`]: crate::convert::TryInto
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[unstable(feature = "try_trait", issue = "42327")]
pub enum UnequalOrdering {
/// An ordering where a compared value is less than another.
Less = -1,
/// An ordering where a compared value is greater than another.
Greater = 1,
}

/// The error type returned when conversion of [`Ordering::Equal`] into
/// [`UnequalOrdering`] fails.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[unstable(feature = "try_trait", issue = "42327")]
pub struct EqualOrderingError;

#[unstable(feature = "try_trait", issue = "42327")]
impl TryFrom<Ordering> for UnequalOrdering {
type Error = EqualOrderingError;

fn try_from(value: Ordering) -> Result<Self, EqualOrderingError> {
match value {
Less => Ok(UnequalOrdering::Less),
Equal => Err(EqualOrderingError),
Greater => Ok(UnequalOrdering::Greater),
}
}
}

#[unstable(feature = "try_trait", issue = "42327")]
impl From<UnequalOrdering> for Ordering {
fn from(value: UnequalOrdering) -> Self {
match value {
UnequalOrdering::Less => Less,
UnequalOrdering::Greater => Greater,
}
}
}

#[unstable(feature = "try_trait", issue = "42327")]
impl ops::Try for Ordering {
type Ok = ();
type Error = UnequalOrdering;

#[inline]
fn into_result(self) -> Result<(), UnequalOrdering> {
match UnequalOrdering::try_from(self) {
Ok(unequal_ordering) => Err(unequal_ordering),
Err(_) => Ok(()),
}
}

#[inline]
fn from_ok(_: ()) -> Self {
Equal
}

#[inline]
fn from_error(v: UnequalOrdering) -> Self {
Self::from(v)
}
}

/// Trait for values that can be compared for a sort-order.
///
/// The comparison must satisfy, for all `a`, `b` and `c`:
Expand Down
89 changes: 89 additions & 0 deletions src/test/ui/ordering-try.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// run-pass

use std::cmp::Ordering;
use std::iter;

#[derive(Eq, PartialEq)]
struct Test(bool, u8, &'static str);

const BOOL_VALUES: [bool; 2] = [true, false];
const U8_VALUES: [u8; 3] = [1, 2, 3];
const STR_VALUES: [&str; 3] = ["a", "ab", "c"];

fn test_values() -> impl Iterator<Item = Test> {
BOOL_VALUES.iter().flat_map(|&a| {
U8_VALUES.iter().flat_map(move |&b| {
STR_VALUES.iter().map(move |&c| Test(a, b, c))
})
})
}

// This Ord implementation should behave the same as #[derive(Ord)],
// but uses the Try operator on Ordering values
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// but uses the Try operator on Ordering values
// but uses the `?` operator on Ordering values

For clarity

impl Ord for Test {
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)?;
self.1.cmp(&other.1)?;
self.2.cmp(&other.2)
}
}

impl PartialOrd for Test {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

fn test_struct_cmp() {
for test1 in test_values() {
let test1_alt = (test1.0, test1.1, test1.2);
for test2 in test_values() {
let test2_alt = (test2.0, test2.1, test2.2);
assert_eq!(test1.cmp(&test2), test1_alt.cmp(&test2_alt));
}
}
}

// Implement Iterator::cmp() using the Try operator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Implement Iterator::cmp() using the Try operator
// Implement Iterator::cmp() using the `?` operator

For clarity.

fn cmp<A, I1, I2>(mut iter1: I1, mut iter2: I2) -> Ordering
where
A: Ord,
I1: Iterator<Item = A>,
I2: Iterator<Item = A>,
{
loop {
match (iter1.next(), iter2.next()) {
(Some(x), Some(y)) => x.cmp(&y)?,
(x, y) => return x.cmp(&y),
}
}
}

fn u8_sequences() -> impl Iterator<Item = Vec<u8>> {
iter::once(vec![])
.chain(U8_VALUES.iter().map(|&a| vec![a]))
.chain(U8_VALUES.iter().flat_map(|&a| {
U8_VALUES.iter().map(move |&b| vec![a, b])
}))
.chain(U8_VALUES.iter().flat_map(|&a| {
U8_VALUES.iter().flat_map(move |&b| {
U8_VALUES.iter().map(move |&c| vec![a, b, c])
})
}))
}

fn test_slice_cmp() {
for sequence1 in u8_sequences() {
for sequence2 in u8_sequences() {
assert_eq!(
cmp(sequence1.iter().copied(), sequence2.iter().copied()),
sequence1.iter().copied().cmp(sequence2.iter().copied()),
);
}
}
}

fn main() {
test_struct_cmp();
test_slice_cmp();
}