Skip to content

Commit 1c882cc

Browse files
committed
Auto merge of #150075 - Kyuuhachi:limit_to, r=joshtriplett,ChrisDenton
Implement clamp_to Implements the revised version of #147781. Supersedes #147786. Currently I restrict the ClampBounds trait using a second, perma-unstable feature. I don't know if that's the usual way to deal with this kind of traits, I'd be happy to change it if not. ~~I currently define NaN as equal to no bound. This is consistent with `max` and `min`, but is inconsistent with `clamp`, which panics.~~ Changed so that the float versions panic if any bound is NaN, just like `clamp` does.
2 parents e2b71ad + f31860e commit 1c882cc

8 files changed

Lines changed: 315 additions & 0 deletions

File tree

library/core/src/cmp.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@
2626
#![stable(feature = "rust1", since = "1.0.0")]
2727

2828
mod bytewise;
29+
mod clamp;
2930
pub(crate) use bytewise::BytewiseEq;
31+
#[unstable(feature = "clamp_bounds", issue = "147781")]
32+
pub use clamp::ClampBounds;
3033

3134
use self::Ordering::*;
3235
use crate::marker::{Destruct, PointeeSized};
@@ -1109,6 +1112,35 @@ pub const trait Ord: [const] Eq + [const] PartialOrd<Self> + PointeeSized {
11091112
self
11101113
}
11111114
}
1115+
1116+
/// Restrict a value to a certain range.
1117+
///
1118+
/// This is equal to `max`, `min`, or `clamp`, depending on whether the range is `min..`,
1119+
/// `..=max`, or `min..=max`, respectively. Exclusive ranges are not permitted.
1120+
///
1121+
/// # Panics
1122+
///
1123+
/// Panics on `min..=max` if `min > max`.
1124+
///
1125+
/// # Examples
1126+
///
1127+
/// ```
1128+
/// #![feature(clamp_to)]
1129+
/// assert_eq!((-3).clamp_to(-2..=1), -2);
1130+
/// assert_eq!(0.clamp_to(-2..=1), 0);
1131+
/// assert_eq!(2.clamp_to(..=1), 1);
1132+
/// assert_eq!(5.clamp_to(7..), 7);
1133+
/// ```
1134+
#[must_use]
1135+
#[inline]
1136+
#[unstable(feature = "clamp_to", issue = "147781")]
1137+
fn clamp_to<R>(self, range: R) -> Self
1138+
where
1139+
Self: Sized + [const] Destruct,
1140+
R: [const] ClampBounds<Self>,
1141+
{
1142+
range.clamp(self)
1143+
}
11121144
}
11131145

11141146
/// Derive macro generating an impl of the trait [`Ord`].

library/core/src/cmp/clamp.rs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
use crate::marker::Destruct;
2+
use crate::ops::{RangeFrom, RangeFull, RangeInclusive, RangeToInclusive};
3+
4+
/// Trait for ranges supported by [`Ord::clamp_to`].
5+
#[unstable(feature = "clamp_bounds", issue = "147781")]
6+
#[rustc_const_unstable(feature = "clamp_bounds", issue = "147781")]
7+
pub const trait ClampBounds<T>: Sized {
8+
/// The implementation of [`Ord::clamp_to`].
9+
fn clamp(self, value: T) -> T
10+
where
11+
T: [const] Destruct;
12+
}
13+
14+
#[unstable(feature = "clamp_bounds", issue = "147781")]
15+
#[rustc_const_unstable(feature = "clamp_bounds", issue = "147781")]
16+
const impl<T> ClampBounds<T> for RangeFrom<T>
17+
where
18+
T: [const] Ord,
19+
{
20+
fn clamp(self, value: T) -> T
21+
where
22+
T: [const] Destruct,
23+
{
24+
value.max(self.start)
25+
}
26+
}
27+
28+
#[unstable(feature = "clamp_bounds", issue = "147781")]
29+
#[rustc_const_unstable(feature = "clamp_bounds", issue = "147781")]
30+
const impl<T> ClampBounds<T> for RangeToInclusive<T>
31+
where
32+
T: [const] Ord,
33+
{
34+
fn clamp(self, value: T) -> T
35+
where
36+
T: [const] Destruct,
37+
{
38+
value.min(self.end)
39+
}
40+
}
41+
42+
#[unstable(feature = "clamp_bounds", issue = "147781")]
43+
#[rustc_const_unstable(feature = "clamp_bounds", issue = "147781")]
44+
const impl<T> ClampBounds<T> for RangeInclusive<T>
45+
where
46+
T: [const] Ord,
47+
{
48+
fn clamp(self, value: T) -> T
49+
where
50+
T: [const] Destruct,
51+
{
52+
let (start, end) = self.into_inner();
53+
value.clamp(start, end)
54+
}
55+
}
56+
57+
#[unstable(feature = "clamp_bounds", issue = "147781")]
58+
#[rustc_const_unstable(feature = "clamp_bounds", issue = "147781")]
59+
const impl<T> ClampBounds<T> for RangeFull {
60+
fn clamp(self, value: T) -> T {
61+
value
62+
}
63+
}
64+
65+
macro impl_for_float($t:ty) {
66+
#[unstable(feature = "clamp_bounds", issue = "147781")]
67+
#[rustc_const_unstable(feature = "clamp_bounds", issue = "147781")]
68+
const impl ClampBounds<$t> for RangeFrom<$t> {
69+
fn clamp(self, value: $t) -> $t {
70+
assert!(!self.start.is_nan(), "start was NaN");
71+
value.max(self.start)
72+
}
73+
}
74+
75+
#[unstable(feature = "clamp_bounds", issue = "147781")]
76+
#[rustc_const_unstable(feature = "clamp_bounds", issue = "147781")]
77+
const impl ClampBounds<$t> for RangeToInclusive<$t> {
78+
fn clamp(self, value: $t) -> $t {
79+
assert!(!self.end.is_nan(), "end was NaN");
80+
value.min(self.end)
81+
}
82+
}
83+
84+
#[unstable(feature = "clamp_bounds", issue = "147781")]
85+
#[rustc_const_unstable(feature = "clamp_bounds", issue = "147781")]
86+
const impl ClampBounds<$t> for RangeInclusive<$t> {
87+
fn clamp(self, value: $t) -> $t {
88+
let (start, end) = self.into_inner();
89+
assert!(start <= end, "start > end, or either was NaN");
90+
value.clamp(start, end)
91+
}
92+
}
93+
}
94+
95+
// #[unstable(feature = "f16", issue = "116909")]
96+
impl_for_float!(f16);
97+
impl_for_float!(f32);
98+
impl_for_float!(f64);
99+
// #[unstable(feature = "f128", issue = "116909")]
100+
impl_for_float!(f128);

library/core/src/num/f128.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1455,6 +1455,41 @@ impl f128 {
14551455
self.clamp(-limit, limit)
14561456
}
14571457

1458+
/// Restrict a value to a certain range, unless it is NaN.
1459+
///
1460+
/// This is largely equal to `max`, `min`, or `clamp`, depending on whether the range is
1461+
/// `min..`, `..=max`, or `min..=max`, respectively. However, unlike `max` and `min`, it will
1462+
/// panic if any bound is NaN.
1463+
///
1464+
/// Note that this function returns NaN if the initial value was NaN as
1465+
/// well.
1466+
///
1467+
/// Exclusive ranges are not permitted.
1468+
///
1469+
/// # Panics
1470+
///
1471+
/// Panics on `min..=max` if `min > max`, or if any bound is NaN.
1472+
///
1473+
/// # Examples
1474+
///
1475+
/// ```
1476+
/// #![feature(f128, clamp_to)]
1477+
/// assert_eq!((-3.0f128).clamp_to(-2.0..=1.0), -2.0);
1478+
/// assert_eq!(0.0f128.clamp_to(-2.0..=1.0), 0.0);
1479+
/// assert_eq!(2.0f128.clamp_to(..=1.0), 1.0);
1480+
/// assert_eq!(5.0f128.clamp_to(7.0..), 7.0);
1481+
/// assert!(f128::NAN.clamp_to(1.0..=2.0).is_nan());
1482+
/// ```
1483+
#[must_use]
1484+
#[inline]
1485+
#[unstable(feature = "clamp_to", issue = "147781")]
1486+
pub fn clamp_to<R>(self, range: R) -> Self
1487+
where
1488+
R: crate::cmp::ClampBounds<Self>,
1489+
{
1490+
range.clamp(self)
1491+
}
1492+
14581493
/// Computes the absolute value of `self`.
14591494
///
14601495
/// This function always returns the precise result.

library/core/src/num/f16.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1441,6 +1441,41 @@ impl f16 {
14411441
self.clamp(-limit, limit)
14421442
}
14431443

1444+
/// Restrict a value to a certain range, unless it is NaN.
1445+
///
1446+
/// This is largely equal to `max`, `min`, or `clamp`, depending on whether the range is
1447+
/// `min..`, `..=max`, or `min..=max`, respectively. However, unlike `max` and `min`, it will
1448+
/// panic if any bound is NaN.
1449+
///
1450+
/// Note that this function returns NaN if the initial value was NaN as
1451+
/// well.
1452+
///
1453+
/// Exclusive ranges are not permitted.
1454+
///
1455+
/// # Panics
1456+
///
1457+
/// Panics on `min..=max` if `min > max`, or if any bound is NaN.
1458+
///
1459+
/// # Examples
1460+
///
1461+
/// ```
1462+
/// #![feature(f16, clamp_to)]
1463+
/// assert_eq!((-3.0f16).clamp_to(-2.0..=1.0), -2.0);
1464+
/// assert_eq!(0.0f16.clamp_to(-2.0..=1.0), 0.0);
1465+
/// assert_eq!(2.0f16.clamp_to(..=1.0), 1.0);
1466+
/// assert_eq!(5.0f16.clamp_to(7.0..), 7.0);
1467+
/// assert!(f16::NAN.clamp_to(1.0..=2.0).is_nan());
1468+
/// ```
1469+
#[must_use]
1470+
#[inline]
1471+
#[unstable(feature = "clamp_to", issue = "147781")]
1472+
pub fn clamp_to<R>(self, range: R) -> Self
1473+
where
1474+
R: crate::cmp::ClampBounds<Self>,
1475+
{
1476+
range.clamp(self)
1477+
}
1478+
14441479
/// Computes the absolute value of `self`.
14451480
///
14461481
/// This function always returns the precise result.

library/core/src/num/f32.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,6 +1609,41 @@ impl f32 {
16091609
self.clamp(-limit, limit)
16101610
}
16111611

1612+
/// Restrict a value to a certain range, unless it is NaN.
1613+
///
1614+
/// This is largely equal to `max`, `min`, or `clamp`, depending on whether the range is
1615+
/// `min..`, `..=max`, or `min..=max`, respectively. However, unlike `max` and `min`, it will
1616+
/// panic if any bound is NaN.
1617+
///
1618+
/// Note that this function returns NaN if the initial value was NaN as
1619+
/// well.
1620+
///
1621+
/// Exclusive ranges are not permitted.
1622+
///
1623+
/// # Panics
1624+
///
1625+
/// Panics on `min..=max` if `min > max`, or if any bound is NaN.
1626+
///
1627+
/// # Examples
1628+
///
1629+
/// ```
1630+
/// #![feature(clamp_to)]
1631+
/// assert_eq!((-3.0f32).clamp_to(-2.0..=1.0), -2.0);
1632+
/// assert_eq!(0.0f32.clamp_to(-2.0..=1.0), 0.0);
1633+
/// assert_eq!(2.0f32.clamp_to(..=1.0), 1.0);
1634+
/// assert_eq!(5.0f32.clamp_to(7.0..), 7.0);
1635+
/// assert!(f32::NAN.clamp_to(1.0..=2.0).is_nan());
1636+
/// ```
1637+
#[must_use]
1638+
#[inline]
1639+
#[unstable(feature = "clamp_to", issue = "147781")]
1640+
pub fn clamp_to<R>(self, range: R) -> Self
1641+
where
1642+
R: crate::cmp::ClampBounds<Self>,
1643+
{
1644+
range.clamp(self)
1645+
}
1646+
16121647
/// Computes the absolute value of `self`.
16131648
///
16141649
/// This function always returns the precise result.

library/core/src/num/f64.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1589,6 +1589,41 @@ impl f64 {
15891589
self.clamp(-limit, limit)
15901590
}
15911591

1592+
/// Restrict a value to a certain range, unless it is NaN.
1593+
///
1594+
/// This is largely equal to `max`, `min`, or `clamp`, depending on whether the range is
1595+
/// `min..`, `..=max`, or `min..=max`, respectively. However, unlike `max` and `min`, it will
1596+
/// panic if any bound is NaN.
1597+
///
1598+
/// Note that this function returns NaN if the initial value was NaN as
1599+
/// well.
1600+
///
1601+
/// Exclusive ranges are not permitted.
1602+
///
1603+
/// # Panics
1604+
///
1605+
/// Panics on `min..=max` if `min > max`, or if any bound is NaN.
1606+
///
1607+
/// # Examples
1608+
///
1609+
/// ```
1610+
/// #![feature(clamp_to)]
1611+
/// assert_eq!((-3.0f64).clamp_to(-2.0..=1.0), -2.0);
1612+
/// assert_eq!(0.0f64.clamp_to(-2.0..=1.0), 0.0);
1613+
/// assert_eq!(2.0f64.clamp_to(..=1.0), 1.0);
1614+
/// assert_eq!(5.0f64.clamp_to(7.0..), 7.0);
1615+
/// assert!(f64::NAN.clamp_to(1.0..=2.0).is_nan());
1616+
/// ```
1617+
#[must_use]
1618+
#[inline]
1619+
#[unstable(feature = "clamp_to", issue = "147781")]
1620+
pub fn clamp_to<R>(self, range: R) -> Self
1621+
where
1622+
R: crate::cmp::ClampBounds<Self>,
1623+
{
1624+
range.clamp(self)
1625+
}
1626+
15921627
/// Computes the absolute value of `self`.
15931628
///
15941629
/// This function always returns the precise result.

library/coretests/tests/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#![feature(casefold)]
1414
#![feature(cfg_target_has_reliable_f16_f128)]
1515
#![feature(char_internals)]
16+
#![feature(clamp_to)]
1617
#![feature(clone_to_uninit)]
1718
#![feature(cmp_minmax)]
1819
#![feature(const_array)]

library/coretests/tests/num/floats.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1359,6 +1359,48 @@ float_test! {
13591359
}
13601360
}
13611361

1362+
float_test! {
1363+
name: clamp_to_min_greater_than_max,
1364+
attrs: {
1365+
const: #[cfg(false)],
1366+
f16: #[should_panic, cfg(target_has_reliable_f16)],
1367+
f32: #[should_panic],
1368+
f64: #[should_panic],
1369+
f128: #[should_panic, cfg(target_has_reliable_f128)],
1370+
},
1371+
test {
1372+
let _ = Float::ONE.clamp_to(3.0..=1.0);
1373+
}
1374+
}
1375+
1376+
float_test! {
1377+
name: clamp_to_min_is_nan,
1378+
attrs: {
1379+
const: #[cfg(false)],
1380+
f16: #[should_panic, cfg(target_has_reliable_f16)],
1381+
f32: #[should_panic],
1382+
f64: #[should_panic],
1383+
f128: #[should_panic, cfg(target_has_reliable_f128)],
1384+
},
1385+
test {
1386+
let _ = Float::ONE.clamp_to(Float::NAN..=1.0);
1387+
}
1388+
}
1389+
1390+
float_test! {
1391+
name: clamp_to_max_is_nan,
1392+
attrs: {
1393+
const: #[cfg(false)],
1394+
f16: #[should_panic, cfg(target_has_reliable_f16)],
1395+
f32: #[should_panic],
1396+
f64: #[should_panic],
1397+
f128: #[should_panic, cfg(target_has_reliable_f128)],
1398+
},
1399+
test {
1400+
let _ = Float::ONE.clamp_to(3.0..=Float::NAN);
1401+
}
1402+
}
1403+
13621404
float_test! {
13631405
name: total_cmp,
13641406
attrs: {

0 commit comments

Comments
 (0)