Skip to content

Commit f6bd6b4

Browse files
committed
rollup merge of #23878: Ryman/stable_extremes
`min`-like functions now return the leftmost element/input for equal elements. `max`-like return the rightmost. Closes #23687. cc @HeroesGrave, @aturon, @alexcrichton
2 parents d03120a + 2a89d69 commit f6bd6b4

File tree

3 files changed

+127
-28
lines changed

3 files changed

+127
-28
lines changed

src/libcore/cmp.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,8 @@ pub trait PartialOrd<Rhs: ?Sized = Self>: PartialEq<Rhs> {
360360

361361
/// Compare and return the minimum of two values.
362362
///
363+
/// Returns the first argument if the comparison determines them to be equal.
364+
///
363365
/// # Examples
364366
///
365367
/// ```
@@ -371,11 +373,13 @@ pub trait PartialOrd<Rhs: ?Sized = Self>: PartialEq<Rhs> {
371373
#[inline]
372374
#[stable(feature = "rust1", since = "1.0.0")]
373375
pub fn min<T: Ord>(v1: T, v2: T) -> T {
374-
if v1 < v2 { v1 } else { v2 }
376+
if v1 <= v2 { v1 } else { v2 }
375377
}
376378

377379
/// Compare and return the maximum of two values.
378380
///
381+
/// Returns the second argument if the comparison determines them to be equal.
382+
///
379383
/// # Examples
380384
///
381385
/// ```
@@ -387,7 +391,7 @@ pub fn min<T: Ord>(v1: T, v2: T) -> T {
387391
#[inline]
388392
#[stable(feature = "rust1", since = "1.0.0")]
389393
pub fn max<T: Ord>(v1: T, v2: T) -> T {
390-
if v1 > v2 { v1 } else { v2 }
394+
if v2 >= v1 { v2 } else { v1 }
391395
}
392396

393397
/// Compare and return the minimum of two values if there is one.
@@ -425,7 +429,7 @@ pub fn partial_min<T: PartialOrd>(v1: T, v2: T) -> Option<T> {
425429

426430
/// Compare and return the maximum of two values if there is one.
427431
///
428-
/// Returns the first argument if the comparison determines them to be equal.
432+
/// Returns the second argument if the comparison determines them to be equal.
429433
///
430434
/// # Examples
431435
///
@@ -450,8 +454,8 @@ pub fn partial_min<T: PartialOrd>(v1: T, v2: T) -> Option<T> {
450454
#[unstable(feature = "core")]
451455
pub fn partial_max<T: PartialOrd>(v1: T, v2: T) -> Option<T> {
452456
match v1.partial_cmp(&v2) {
453-
Some(Less) => Some(v2),
454-
Some(Equal) | Some(Greater) => Some(v1),
457+
Some(Equal) | Some(Less) => Some(v2),
458+
Some(Greater) => Some(v1),
455459
None => None
456460
}
457461
}

src/libcore/iter.rs

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,9 @@ pub trait Iterator {
722722

723723
/// Consumes the entire iterator to return the maximum element.
724724
///
725+
/// Returns the rightmost element if the comparison determines two elements
726+
/// to be equally maximum.
727+
///
725728
/// # Examples
726729
///
727730
/// ```
@@ -732,16 +735,19 @@ pub trait Iterator {
732735
#[stable(feature = "rust1", since = "1.0.0")]
733736
fn max(self) -> Option<Self::Item> where Self: Sized, Self::Item: Ord
734737
{
735-
self.fold(None, |max, x| {
738+
self.fold(None, |max, y| {
736739
match max {
737-
None => Some(x),
738-
Some(y) => Some(cmp::max(x, y))
740+
None => Some(y),
741+
Some(x) => Some(cmp::max(x, y))
739742
}
740743
})
741744
}
742745

743746
/// Consumes the entire iterator to return the minimum element.
744747
///
748+
/// Returns the leftmost element if the comparison determines two elements
749+
/// to be equally minimum.
750+
///
745751
/// # Examples
746752
///
747753
/// ```
@@ -752,10 +758,10 @@ pub trait Iterator {
752758
#[stable(feature = "rust1", since = "1.0.0")]
753759
fn min(self) -> Option<Self::Item> where Self: Sized, Self::Item: Ord
754760
{
755-
self.fold(None, |min, x| {
761+
self.fold(None, |min, y| {
756762
match min {
757-
None => Some(x),
758-
Some(y) => Some(cmp::min(x, y))
763+
None => Some(y),
764+
Some(x) => Some(cmp::min(x, y))
759765
}
760766
})
761767
}
@@ -799,7 +805,7 @@ pub trait Iterator {
799805
Some(x) => {
800806
match self.next() {
801807
None => return OneElement(x),
802-
Some(y) => if x < y {(x, y)} else {(y,x)}
808+
Some(y) => if x <= y {(x, y)} else {(y, x)}
803809
}
804810
}
805811
};
@@ -817,19 +823,19 @@ pub trait Iterator {
817823
None => {
818824
if first < min {
819825
min = first;
820-
} else if first > max {
826+
} else if first >= max {
821827
max = first;
822828
}
823829
break;
824830
}
825831
Some(x) => x
826832
};
827-
if first < second {
828-
if first < min {min = first;}
829-
if max < second {max = second;}
833+
if first <= second {
834+
if first < min { min = first }
835+
if second >= max { max = second }
830836
} else {
831-
if second < min {min = second;}
832-
if max < first {max = first;}
837+
if second < min { min = second }
838+
if first >= max { max = first }
833839
}
834840
}
835841

@@ -839,6 +845,9 @@ pub trait Iterator {
839845
/// Return the element that gives the maximum value from the
840846
/// specified function.
841847
///
848+
/// Returns the rightmost element if the comparison determines two elements
849+
/// to be equally maximum.
850+
///
842851
/// # Examples
843852
///
844853
/// ```
@@ -854,14 +863,14 @@ pub trait Iterator {
854863
Self: Sized,
855864
F: FnMut(&Self::Item) -> B,
856865
{
857-
self.fold(None, |max: Option<(Self::Item, B)>, x| {
858-
let x_val = f(&x);
866+
self.fold(None, |max: Option<(Self::Item, B)>, y| {
867+
let y_val = f(&y);
859868
match max {
860-
None => Some((x, x_val)),
861-
Some((y, y_val)) => if x_val > y_val {
862-
Some((x, x_val))
863-
} else {
869+
None => Some((y, y_val)),
870+
Some((x, x_val)) => if y_val >= x_val {
864871
Some((y, y_val))
872+
} else {
873+
Some((x, x_val))
865874
}
866875
}
867876
}).map(|(x, _)| x)
@@ -870,6 +879,9 @@ pub trait Iterator {
870879
/// Return the element that gives the minimum value from the
871880
/// specified function.
872881
///
882+
/// Returns the leftmost element if the comparison determines two elements
883+
/// to be equally minimum.
884+
///
873885
/// # Examples
874886
///
875887
/// ```
@@ -885,11 +897,11 @@ pub trait Iterator {
885897
Self: Sized,
886898
F: FnMut(&Self::Item) -> B,
887899
{
888-
self.fold(None, |min: Option<(Self::Item, B)>, x| {
889-
let x_val = f(&x);
900+
self.fold(None, |min: Option<(Self::Item, B)>, y| {
901+
let y_val = f(&y);
890902
match min {
891-
None => Some((x, x_val)),
892-
Some((y, y_val)) => if x_val < y_val {
903+
None => Some((y, y_val)),
904+
Some((x, x_val)) => if x_val <= y_val {
893905
Some((x, x_val))
894906
} else {
895907
Some((y, y_val))
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(core)]
12+
use std::fmt::Debug;
13+
use std::cmp::{self, PartialOrd, Ordering};
14+
use std::iter::MinMaxResult::MinMax;
15+
16+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
17+
struct Foo {
18+
n: u8,
19+
name: &'static str
20+
}
21+
22+
impl PartialOrd for Foo {
23+
fn partial_cmp(&self, other: &Foo) -> Option<Ordering> {
24+
Some(self.cmp(other))
25+
}
26+
}
27+
28+
impl Ord for Foo {
29+
fn cmp(&self, other: &Foo) -> Ordering {
30+
self.n.cmp(&other.n)
31+
}
32+
}
33+
34+
fn main() {
35+
let a = Foo { n: 4, name: "a" };
36+
let b = Foo { n: 4, name: "b" };
37+
let c = Foo { n: 8, name: "c" };
38+
let d = Foo { n: 8, name: "d" };
39+
let e = Foo { n: 22, name: "e" };
40+
let f = Foo { n: 22, name: "f" };
41+
42+
let data = [a, b, c, d, e, f];
43+
44+
// `min` should return the left when the values are equal
45+
assert_eq!(data.iter().min(), Some(&a));
46+
assert_eq!(data.iter().min_by(|a| a.n), Some(&a));
47+
assert_eq!(cmp::min(a, b), a);
48+
assert_eq!(cmp::min(b, a), b);
49+
assert_eq!(cmp::partial_min(a, b), Some(a));
50+
assert_eq!(cmp::partial_min(b, a), Some(b));
51+
52+
// `max` should return the right when the values are equal
53+
assert_eq!(data.iter().max(), Some(&f));
54+
assert_eq!(data.iter().max_by(|a| a.n), Some(&f));
55+
assert_eq!(cmp::max(e, f), f);
56+
assert_eq!(cmp::max(f, e), e);
57+
assert_eq!(cmp::partial_max(e, f), Some(f));
58+
assert_eq!(cmp::partial_max(f, e), Some(e));
59+
60+
// Similar for `min_max`
61+
assert_eq!(data.iter().min_max(), MinMax(&a, &f));
62+
assert_eq!(data[1..5].iter().min_max(), MinMax(&b, &e));
63+
assert_eq!(data[2..4].iter().min_max(), MinMax(&c, &d));
64+
65+
let mut presorted = data.to_vec();
66+
presorted.sort();
67+
assert_stable(&presorted);
68+
69+
let mut presorted = data.to_vec();
70+
presorted.sort_by(|a, b| a.cmp(b));
71+
assert_stable(&presorted);
72+
73+
// Assert that sorted and min/max are the same
74+
fn assert_stable<T: Ord + Debug>(presorted: &[T]) {
75+
for slice in presorted.windows(2) {
76+
let a = &slice[0];
77+
let b = &slice[1];
78+
79+
assert_eq!(a, cmp::min(a, b));
80+
assert_eq!(b, cmp::max(a, b));
81+
}
82+
}
83+
}

0 commit comments

Comments
 (0)