Skip to content
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
50 changes: 42 additions & 8 deletions library/core/src/iter/traits/double_ended.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,13 +189,49 @@ pub const trait DoubleEndedIterator: [const] Iterator {
where
Self::Item: [const] Destruct,
{
for i in 0..n {
if self.next_back().is_none() {
// SAFETY: `i` is always less than `n`.
return Err(unsafe { NonZero::new_unchecked(n - i) });
/// Helper trait to specialize `advance_back_by` via `try_rfold` for `Sized` iterators.

#[rustc_const_unstable(feature = "const_iter", issue = "92476")]
const trait SpecAdvanceBackBy {
fn spec_advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>>;
}

#[rustc_const_unstable(feature = "const_iter", issue = "92476")]
const impl<I: [const] DoubleEndedIterator + ?Sized> SpecAdvanceBackBy for I
where
I::Item: [const] Destruct,
{
default fn spec_advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
for i in 0..n {
if self.next_back().is_none() {
// SAFETY: `i` is always less than `n`.
return Err(unsafe { NonZero::new_unchecked(n - i) });
}
}
Ok(())
}
}
Ok(())

#[rustc_const_unstable(feature = "const_iter", issue = "92476")]
const impl<I: [const] DoubleEndedIterator> SpecAdvanceBackBy for I
where
I::Item: [const] Destruct,
{
fn spec_advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
let Some(n) = NonZero::new(n) else {
return Ok(());
};

let res = self.try_rfold(n, const |n, _| NonZero::new(n.get() - 1));

match res {
None => Ok(()),
Some(n) => Err(n),
}
}
}

self.spec_advance_back_by(n)
}

/// Returns the `n`th element from the end of the iterator.
Expand Down Expand Up @@ -245,9 +281,7 @@ pub const trait DoubleEndedIterator: [const] Iterator {
where
Self::Item: [const] Destruct,
{
if self.advance_back_by(n).is_err() {
return None;
}
self.advance_back_by(n).ok()?;
self.next_back()
}

Expand Down
5 changes: 3 additions & 2 deletions library/core/src/num/f128.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1544,9 +1544,10 @@ impl f128 {
/// ```
#[inline]
#[unstable(feature = "clamp_magnitude", issue = "148519")]
#[must_use = "this returns the clamped value and does not modify the original"]
#[rustc_const_unstable(feature = "clamp_magnitude", issue = "148519")]
#[must_use = "method returns a new number and does not mutate the original value"]
#[expect(clippy::neg_cmp_op_on_partial_ord, reason = "NaN is also invalid")]
pub fn clamp_magnitude(self, limit: f128) -> f128 {
pub const fn clamp_magnitude(self, limit: f128) -> f128 {
assert!(limit >= 0.0, "limit must be non-negative and not NaN");
let limit = limit.abs(); // Canonicalises -0.0 to 0.0
self.clamp(-limit, limit)
Expand Down
5 changes: 3 additions & 2 deletions library/core/src/num/f16.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1530,9 +1530,10 @@ impl f16 {
/// ```
#[inline]
#[unstable(feature = "clamp_magnitude", issue = "148519")]
#[must_use = "this returns the clamped value and does not modify the original"]
#[rustc_const_unstable(feature = "clamp_magnitude", issue = "148519")]
#[must_use = "method returns a new number and does not mutate the original value"]
#[expect(clippy::neg_cmp_op_on_partial_ord, reason = "NaN is also invalid")]
pub fn clamp_magnitude(self, limit: f16) -> f16 {
pub const fn clamp_magnitude(self, limit: f16) -> f16 {
assert!(limit >= 0.0, "limit must be non-negative and not NaN");
let limit = limit.abs(); // Canonicalises -0.0 to 0.0
self.clamp(-limit, limit)
Expand Down
6 changes: 3 additions & 3 deletions library/core/src/num/f32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1699,11 +1699,11 @@ impl f32 {
/// assert_eq!(2.0f32.clamp_magnitude(3.0), 2.0);
/// assert_eq!((-2.0f32).clamp_magnitude(3.0), -2.0);
/// ```
#[must_use = "this returns the clamped value and does not modify the original"]
#[unstable(feature = "clamp_magnitude", issue = "148519")]
#[inline]
#[unstable(feature = "clamp_magnitude", issue = "148519")]
#[must_use = "method returns a new number and does not mutate the original value"]
#[expect(clippy::neg_cmp_op_on_partial_ord, reason = "NaN is also invalid")]
pub fn clamp_magnitude(self, limit: f32) -> f32 {
pub const fn clamp_magnitude(self, limit: f32) -> f32 {
assert!(limit >= 0.0, "limit must be non-negative and not NaN");
let limit = limit.abs(); // Canonicalises -0.0 to 0.0
self.clamp(-limit, limit)
Expand Down
6 changes: 3 additions & 3 deletions library/core/src/num/f64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1677,11 +1677,11 @@ impl f64 {
/// assert_eq!(2.0f64.clamp_magnitude(3.0), 2.0);
/// assert_eq!((-2.0f64).clamp_magnitude(3.0), -2.0);
/// ```
#[must_use = "this returns the clamped value and does not modify the original"]
#[unstable(feature = "clamp_magnitude", issue = "148519")]
#[inline]
#[unstable(feature = "clamp_magnitude", issue = "148519")]
#[must_use = "method returns a new number and does not mutate the original value"]
#[expect(clippy::neg_cmp_op_on_partial_ord, reason = "NaN is also invalid")]
pub fn clamp_magnitude(self, limit: f64) -> f64 {
pub const fn clamp_magnitude(self, limit: f64) -> f64 {
assert!(limit >= 0.0, "limit must be non-negative and not NaN");
let limit = limit.abs(); // Canonicalises -0.0 to 0.0
self.clamp(-limit, limit)
Expand Down
11 changes: 6 additions & 5 deletions library/core/src/num/int_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3961,14 +3961,15 @@ macro_rules! int_impl {
/// ```
/// #![feature(clamp_magnitude)]
#[doc = concat!("assert_eq!(120", stringify!($SelfT), ".clamp_magnitude(100), 100);")]
#[doc = concat!("assert_eq!(-120", stringify!($SelfT), ".clamp_magnitude(100), -100);")]
#[doc = concat!("assert_eq!((-120", stringify!($SelfT), ").clamp_magnitude(100), -100);")]
#[doc = concat!("assert_eq!(80", stringify!($SelfT), ".clamp_magnitude(100), 80);")]
#[doc = concat!("assert_eq!(-80", stringify!($SelfT), ".clamp_magnitude(100), -80);")]
#[doc = concat!("assert_eq!((-80", stringify!($SelfT), ").clamp_magnitude(100), -80);")]
/// ```
#[must_use = "this returns the clamped value and does not modify the original"]
#[unstable(feature = "clamp_magnitude", issue = "148519")]
#[inline]
pub fn clamp_magnitude(self, limit: $UnsignedT) -> Self {
#[must_use = "method returns a new number and does not mutate the original value"]
#[rustc_const_unstable(feature = "const_cmp", issue = "143800")]
#[unstable(feature = "clamp_magnitude", issue = "148519")]
pub const fn clamp_magnitude(self, limit: $UnsignedT) -> Self {
if let Ok(limit) = core::convert::TryInto::<$SelfT>::try_into(limit) {
self.clamp(-limit, limit)
} else {
Expand Down
44 changes: 44 additions & 0 deletions library/core/src/num/nonzero.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2612,6 +2612,50 @@ macro_rules! nonzero_integer_signedness_dependent_methods {
unsafe { NonZero::new_unchecked(self.get().cast_unsigned()) }
}

/// Clamps this number to a symmetric range centred around zero.
///
/// The method clamps the number's magnitude (absolute value) to be at most `limit`.
///
/// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more
/// explicit about the intent.
///
/// # Examples
///
/// ```
/// #![feature(clamp_magnitude)]
/// # use std::num::NonZero;
/// #
#[doc = concat!("let limit = NonZero::<", stringify!($Uint), ">::new(100).unwrap();")]
///
/// assert_eq!(
#[doc = concat!(" NonZero::<", stringify!($Int), ">::new(120).unwrap().clamp_magnitude(limit),")]
#[doc = concat!(" NonZero::<", stringify!($Int), ">::new(100).unwrap(),")]
/// );
///
/// assert_eq!(
#[doc = concat!(" NonZero::<", stringify!($Int), ">::new(-120).unwrap().clamp_magnitude(limit),")]
#[doc = concat!(" NonZero::<", stringify!($Int), ">::new(-100).unwrap(),")]
/// );
///
/// assert_eq!(
#[doc = concat!(" NonZero::<", stringify!($Int), ">::new(80).unwrap().clamp_magnitude(limit),")]
#[doc = concat!(" NonZero::<", stringify!($Int), ">::new(80).unwrap(),")]
/// );
///
/// assert_eq!(
#[doc = concat!(" NonZero::<", stringify!($Int), ">::new(-80).unwrap().clamp_magnitude(limit),")]
#[doc = concat!(" NonZero::<", stringify!($Int), ">::new(-80).unwrap(),")]
/// );
/// ```
#[inline]
#[must_use = "method returns a new number and does not mutate the original value"]
#[rustc_const_unstable(feature = "const_cmp", issue = "143800")]
#[unstable(feature = "clamp_magnitude", issue = "148519")]
pub const fn clamp_magnitude(self, limit: NonZero<$Uint>) -> Self {
// SAFETY: a non-zero value clamped to the magnitude of a non-zero value is still non-zero.
unsafe { Self::new_unchecked(self.get().clamp_magnitude(limit.get())) }
}

};
}

Expand Down
1 change: 1 addition & 0 deletions library/coretests/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#![feature(cfg_overflow_checks)]
#![feature(cfg_target_has_reliable_f16_f128)]
#![feature(char_internals)]
#![feature(clamp_magnitude)]
#![feature(clamp_to)]
#![feature(clone_to_uninit)]
#![feature(cmp_minmax)]
Expand Down
Loading
Loading