Skip to content

Commit 3854581

Browse files
Rollup merge of rust-lang#150075 - Kyuuhachi:limit_to, r=ChrisDenton
Implement clamp_to Implements the revised version of rust-lang#147781. Supersedes rust-lang#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 e457a7b + bd174e1 commit 3854581

8 files changed

Lines changed: 331 additions & 8 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};
@@ -1169,6 +1172,35 @@ pub const trait Ord: [const] Eq + [const] PartialOrd<Self> + PointeeSized {
11691172
self
11701173
}
11711174
}
1175+
1176+
/// Restrict a value to a certain range.
1177+
///
1178+
/// This is equal to `max`, `min`, or `clamp`, depending on whether the range is `min..`,
1179+
/// `..=max`, or `min..=max`, respectively. Exclusive ranges are not permitted.
1180+
///
1181+
/// # Panics
1182+
///
1183+
/// Panics on `min..=max` if `min > max`.
1184+
///
1185+
/// # Examples
1186+
///
1187+
/// ```
1188+
/// #![feature(clamp_to)]
1189+
/// assert_eq!((-3).clamp_to(-2..=1), -2);
1190+
/// assert_eq!(0.clamp_to(-2..=1), 0);
1191+
/// assert_eq!(2.clamp_to(..=1), 1);
1192+
/// assert_eq!(5.clamp_to(7..), 7);
1193+
/// ```
1194+
#[must_use]
1195+
#[inline]
1196+
#[unstable(feature = "clamp_to", issue = "147781")]
1197+
fn clamp_to<R>(self, range: R) -> Self
1198+
where
1199+
Self: Sized + [const] Destruct,
1200+
R: [const] ClampBounds<Self>,
1201+
{
1202+
range.clamp(self)
1203+
}
11721204
}
11731205

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

library/core/src/cmp/clamp.rs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
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+
#[expect(
88+
clippy::neg_cmp_op_on_partial_ord,
89+
reason = "NaN check is intentionally included in comparison"
90+
)]
91+
fn clamp(self, value: $t) -> $t {
92+
let (start, end) = self.into_inner();
93+
assert!(start <= end, "start > end, or either was NaN");
94+
value.clamp(start, end)
95+
}
96+
}
97+
}
98+
99+
// #[unstable(feature = "f16", issue = "116909")]
100+
impl_for_float!(f16);
101+
impl_for_float!(f32);
102+
impl_for_float!(f64);
103+
// #[unstable(feature = "f128", issue = "116909")]
104+
impl_for_float!(f128);

library/core/src/num/f128.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1483,7 +1483,7 @@ impl f128 {
14831483
///
14841484
/// ```
14851485
/// #![feature(f128)]
1486-
/// # #[cfg(target_has_reliable_f128)] {
1486+
/// # #[cfg(target_has_reliable_f128_math)] {
14871487
///
14881488
/// assert!((-3.0f128).clamp(-2.0, 1.0) == -2.0);
14891489
/// assert!((0.0f128).clamp(-2.0, 1.0) == 0.0);
@@ -1552,6 +1552,43 @@ impl f128 {
15521552
self.clamp(-limit, limit)
15531553
}
15541554

1555+
/// Restrict a value to a certain range, unless it is NaN.
1556+
///
1557+
/// This is largely equal to `max`, `min`, or `clamp`, depending on whether the range is
1558+
/// `min..`, `..=max`, or `min..=max`, respectively. However, unlike `max` and `min`, it will
1559+
/// panic if any bound is NaN.
1560+
///
1561+
/// Note that this function returns NaN if the initial value was NaN as
1562+
/// well.
1563+
///
1564+
/// Exclusive ranges are not permitted.
1565+
///
1566+
/// # Panics
1567+
///
1568+
/// Panics on `min..=max` if `min > max`, or if any bound is NaN.
1569+
///
1570+
/// # Examples
1571+
///
1572+
/// ```
1573+
/// #![feature(f128, clamp_to)]
1574+
/// # #[cfg(target_has_reliable_f128_math)] {
1575+
/// assert_eq!((-3.0f128).clamp_to(-2.0..=1.0), -2.0);
1576+
/// assert_eq!(0.0f128.clamp_to(-2.0..=1.0), 0.0);
1577+
/// assert_eq!(2.0f128.clamp_to(..=1.0), 1.0);
1578+
/// assert_eq!(5.0f128.clamp_to(7.0..), 7.0);
1579+
/// assert!(f128::NAN.clamp_to(1.0..=2.0).is_nan());
1580+
/// # }
1581+
/// ```
1582+
#[must_use]
1583+
#[inline]
1584+
#[unstable(feature = "clamp_to", issue = "147781")]
1585+
pub fn clamp_to<R>(self, range: R) -> Self
1586+
where
1587+
R: crate::cmp::ClampBounds<Self>,
1588+
{
1589+
range.clamp(self)
1590+
}
1591+
15551592
/// Computes the absolute value of `self`.
15561593
///
15571594
/// This function always returns the precise result.

library/core/src/num/f16.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1469,7 +1469,7 @@ impl f16 {
14691469
///
14701470
/// ```
14711471
/// #![feature(f16)]
1472-
/// # #[cfg(target_has_reliable_f16)] {
1472+
/// # #[cfg(target_has_reliable_f16_math)] {
14731473
///
14741474
/// assert!((-3.0f16).clamp(-2.0, 1.0) == -2.0);
14751475
/// assert!((0.0f16).clamp(-2.0, 1.0) == 0.0);
@@ -1538,6 +1538,43 @@ impl f16 {
15381538
self.clamp(-limit, limit)
15391539
}
15401540

1541+
/// Restrict a value to a certain range, unless it is NaN.
1542+
///
1543+
/// This is largely equal to `max`, `min`, or `clamp`, depending on whether the range is
1544+
/// `min..`, `..=max`, or `min..=max`, respectively. However, unlike `max` and `min`, it will
1545+
/// panic if any bound is NaN.
1546+
///
1547+
/// Note that this function returns NaN if the initial value was NaN as
1548+
/// well.
1549+
///
1550+
/// Exclusive ranges are not permitted.
1551+
///
1552+
/// # Panics
1553+
///
1554+
/// Panics on `min..=max` if `min > max`, or if any bound is NaN.
1555+
///
1556+
/// # Examples
1557+
///
1558+
/// ```
1559+
/// #![feature(f16, clamp_to)]
1560+
/// # #[cfg(target_has_reliable_f16_math)] {
1561+
/// assert_eq!((-3.0f16).clamp_to(-2.0..=1.0), -2.0);
1562+
/// assert_eq!(0.0f16.clamp_to(-2.0..=1.0), 0.0);
1563+
/// assert_eq!(2.0f16.clamp_to(..=1.0), 1.0);
1564+
/// assert_eq!(5.0f16.clamp_to(7.0..), 7.0);
1565+
/// assert!(f16::NAN.clamp_to(1.0..=2.0).is_nan());
1566+
/// # }
1567+
/// ```
1568+
#[must_use]
1569+
#[inline]
1570+
#[unstable(feature = "clamp_to", issue = "147781")]
1571+
pub fn clamp_to<R>(self, range: R) -> Self
1572+
where
1573+
R: crate::cmp::ClampBounds<Self>,
1574+
{
1575+
range.clamp(self)
1576+
}
1577+
15411578
/// Computes the absolute value of `self`.
15421579
///
15431580
/// 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
@@ -1709,6 +1709,41 @@ impl f32 {
17091709
self.clamp(-limit, limit)
17101710
}
17111711

1712+
/// Restrict a value to a certain range, unless it is NaN.
1713+
///
1714+
/// This is largely equal to `max`, `min`, or `clamp`, depending on whether the range is
1715+
/// `min..`, `..=max`, or `min..=max`, respectively. However, unlike `max` and `min`, it will
1716+
/// panic if any bound is NaN.
1717+
///
1718+
/// Note that this function returns NaN if the initial value was NaN as
1719+
/// well.
1720+
///
1721+
/// Exclusive ranges are not permitted.
1722+
///
1723+
/// # Panics
1724+
///
1725+
/// Panics on `min..=max` if `min > max`, or if any bound is NaN.
1726+
///
1727+
/// # Examples
1728+
///
1729+
/// ```
1730+
/// #![feature(clamp_to)]
1731+
/// assert_eq!((-3.0f32).clamp_to(-2.0..=1.0), -2.0);
1732+
/// assert_eq!(0.0f32.clamp_to(-2.0..=1.0), 0.0);
1733+
/// assert_eq!(2.0f32.clamp_to(..=1.0), 1.0);
1734+
/// assert_eq!(5.0f32.clamp_to(7.0..), 7.0);
1735+
/// assert!(f32::NAN.clamp_to(1.0..=2.0).is_nan());
1736+
/// ```
1737+
#[must_use]
1738+
#[inline]
1739+
#[unstable(feature = "clamp_to", issue = "147781")]
1740+
pub fn clamp_to<R>(self, range: R) -> Self
1741+
where
1742+
R: crate::cmp::ClampBounds<Self>,
1743+
{
1744+
range.clamp(self)
1745+
}
1746+
17121747
/// Computes the absolute value of `self`.
17131748
///
17141749
/// 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
@@ -1687,6 +1687,41 @@ impl f64 {
16871687
self.clamp(-limit, limit)
16881688
}
16891689

1690+
/// Restrict a value to a certain range, unless it is NaN.
1691+
///
1692+
/// This is largely equal to `max`, `min`, or `clamp`, depending on whether the range is
1693+
/// `min..`, `..=max`, or `min..=max`, respectively. However, unlike `max` and `min`, it will
1694+
/// panic if any bound is NaN.
1695+
///
1696+
/// Note that this function returns NaN if the initial value was NaN as
1697+
/// well.
1698+
///
1699+
/// Exclusive ranges are not permitted.
1700+
///
1701+
/// # Panics
1702+
///
1703+
/// Panics on `min..=max` if `min > max`, or if any bound is NaN.
1704+
///
1705+
/// # Examples
1706+
///
1707+
/// ```
1708+
/// #![feature(clamp_to)]
1709+
/// assert_eq!((-3.0f64).clamp_to(-2.0..=1.0), -2.0);
1710+
/// assert_eq!(0.0f64.clamp_to(-2.0..=1.0), 0.0);
1711+
/// assert_eq!(2.0f64.clamp_to(..=1.0), 1.0);
1712+
/// assert_eq!(5.0f64.clamp_to(7.0..), 7.0);
1713+
/// assert!(f64::NAN.clamp_to(1.0..=2.0).is_nan());
1714+
/// ```
1715+
#[must_use]
1716+
#[inline]
1717+
#[unstable(feature = "clamp_to", issue = "147781")]
1718+
pub fn clamp_to<R>(self, range: R) -> Self
1719+
where
1720+
R: crate::cmp::ClampBounds<Self>,
1721+
{
1722+
range.clamp(self)
1723+
}
1724+
16901725
/// Computes the absolute value of `self`.
16911726
///
16921727
/// 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
@@ -14,6 +14,7 @@
1414
#![feature(cfg_overflow_checks)]
1515
#![feature(cfg_target_has_reliable_f16_f128)]
1616
#![feature(char_internals)]
17+
#![feature(clamp_to)]
1718
#![feature(clone_to_uninit)]
1819
#![feature(cmp_minmax)]
1920
#![feature(cmp_splat)]

0 commit comments

Comments
 (0)