From 085dff4944882b255671a4a5ca76e05a71e6b625 Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Mon, 2 Mar 2026 21:01:30 -0700 Subject: [PATCH] stabilize new RangeFrom type and iterator stabilizes `core::range::RangeFrom` stabilizes `core::range::RangeFromIter` add examples for `remainder` method on range iterators `RangeFromIter::remainder` was not stabilized (see issue 154458) --- library/core/src/ffi/c_str.rs | 2 +- library/core/src/range.rs | 41 ++++++++----------- library/core/src/range/iter.rs | 41 +++++++++++++++++-- library/core/src/slice/index.rs | 4 +- library/core/src/str/traits.rs | 2 +- .../fromrangeiter-overflow-checks.rs | 1 + .../rangefrom-overflow-2crates-ocno.rs | 1 - .../rangefrom-overflow-2crates-ocyes.rs | 1 - tests/ui/iterators/fromrangeiter.rs | 2 - .../iterators/rangefrom-overflow-2crates.rs | 2 - tests/ui/range/new_range_stability.rs | 24 +++++++++-- tests/ui/range/new_range_stability.stderr | 28 ++++--------- 12 files changed, 90 insertions(+), 59 deletions(-) diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 5fc97d9a69efd..62b3d75d7a779 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -716,7 +716,7 @@ impl ops::Index> for CStr { } } -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_from_api", since = "CURRENT_RUSTC_VERSION")] impl ops::Index> for CStr { type Output = CStr; diff --git a/library/core/src/range.rs b/library/core/src/range.rs index 3c2c40d492005..2007533e68e54 100644 --- a/library/core/src/range.rs +++ b/library/core/src/range.rs @@ -24,12 +24,15 @@ mod iter; #[unstable(feature = "new_range_api", issue = "125687")] pub mod legacy; +#[doc(inline)] +#[stable(feature = "new_range_from_api", since = "CURRENT_RUSTC_VERSION")] +pub use iter::RangeFromIter; #[doc(inline)] #[stable(feature = "new_range_inclusive_api", since = "1.95.0")] pub use iter::RangeInclusiveIter; #[doc(inline)] #[unstable(feature = "new_range_api", issue = "125687")] -pub use iter::{RangeFromIter, RangeIter}; +pub use iter::RangeIter; // FIXME(#125687): re-exports temporarily removed // Because re-exports of stable items (Bound, RangeBounds, RangeFull, RangeTo) @@ -416,14 +419,13 @@ impl const From> for RangeInclusive { /// /// The `RangeFrom` `start..` contains all values with `x >= start`. /// -/// *Note*: Overflow in the [`Iterator`] implementation (when the contained +/// *Note*: Overflow in the [`IntoIterator`] implementation (when the contained /// data type reaches its numerical limit) is allowed to panic, wrap, or /// saturate. This behavior is defined by the implementation of the [`Step`] /// trait. For primitive integers, this follows the normal rules, and respects -/// the overflow checks profile (panic in debug, wrap in release). Note also -/// that overflow happens earlier than you might assume: the overflow happens -/// in the call to `next` that yields the maximum value, as the range must be -/// set to a state to yield the next value. +/// the overflow checks profile (panic in debug, wrap in release). Unlike +/// its legacy counterpart, the iterator will only panic after yielding the +/// maximum value when overflow checks are enabled. /// /// [`Step`]: crate::iter::Step /// @@ -432,7 +434,6 @@ impl const From> for RangeInclusive { /// The `start..` syntax is a `RangeFrom`: /// /// ``` -/// #![feature(new_range_api)] /// use core::range::RangeFrom; /// /// assert_eq!(RangeFrom::from(2..), core::range::RangeFrom { start: 2 }); @@ -441,14 +442,14 @@ impl const From> for RangeInclusive { #[lang = "RangeFromCopy"] #[derive(Copy, Hash)] #[derive_const(Clone, PartialEq, Eq)] -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_from_api", since = "CURRENT_RUSTC_VERSION")] pub struct RangeFrom { /// The lower bound of the range (inclusive). - #[unstable(feature = "new_range_api", issue = "125687")] + #[stable(feature = "new_range_from_api", since = "CURRENT_RUSTC_VERSION")] pub start: Idx, } -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_from_api", since = "CURRENT_RUSTC_VERSION")] impl fmt::Debug for RangeFrom { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { self.start.fmt(fmt)?; @@ -465,7 +466,6 @@ impl RangeFrom { /// # Examples /// /// ``` - /// #![feature(new_range_api)] /// use core::range::RangeFrom; /// /// let mut i = RangeFrom::from(3..).iter().map(|n| n*n); @@ -473,7 +473,7 @@ impl RangeFrom { /// assert_eq!(i.next(), Some(16)); /// assert_eq!(i.next(), Some(25)); /// ``` - #[unstable(feature = "new_range_api", issue = "125687")] + #[stable(feature = "new_range_from_api", since = "CURRENT_RUSTC_VERSION")] #[inline] pub fn iter(&self) -> RangeFromIter { self.clone().into_iter() @@ -486,7 +486,6 @@ impl> RangeFrom { /// # Examples /// /// ``` - /// #![feature(new_range_api)] /// use core::range::RangeFrom; /// /// assert!(!RangeFrom::from(3..).contains(&2)); @@ -498,7 +497,7 @@ impl> RangeFrom { /// assert!(!RangeFrom::from(f32::NAN..).contains(&0.5)); /// ``` #[inline] - #[unstable(feature = "new_range_api", issue = "125687")] + #[stable(feature = "new_range_from_api", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_range", issue = "none")] pub const fn contains(&self, item: &U) -> bool where @@ -509,7 +508,7 @@ impl> RangeFrom { } } -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_from_api", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_range", issue = "none")] impl const RangeBounds for RangeFrom { fn start_bound(&self) -> Bound<&T> { @@ -526,7 +525,7 @@ impl const RangeBounds for RangeFrom { /// If you need to use this implementation where `T` is unsized, /// consider using the `RangeBounds` impl for a 2-tuple of [`Bound<&T>`][Bound], /// i.e. replace `start..` with `(Bound::Included(start), Bound::Unbounded)`. -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_from_api", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_range", issue = "none")] impl const RangeBounds for RangeFrom<&T> { fn start_bound(&self) -> Bound<&T> { @@ -537,8 +536,7 @@ impl const RangeBounds for RangeFrom<&T> { } } -// #[unstable(feature = "range_into_bounds", issue = "136903")] -#[unstable(feature = "new_range_api", issue = "125687")] +#[unstable(feature = "range_into_bounds", issue = "136903")] #[rustc_const_unstable(feature = "const_range", issue = "none")] impl const IntoBounds for RangeFrom { fn into_bounds(self) -> (Bound, Bound) { @@ -547,7 +545,6 @@ impl const IntoBounds for RangeFrom { } #[unstable(feature = "one_sided_range", issue = "69780")] -// #[unstable(feature = "new_range_api", issue = "125687")] #[rustc_const_unstable(feature = "const_range", issue = "none")] impl const OneSidedRange for RangeFrom where @@ -558,7 +555,7 @@ where } } -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_from_api", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_index", issue = "143775")] impl const From> for legacy::RangeFrom { #[inline] @@ -566,7 +563,7 @@ impl const From> for legacy::RangeFrom { Self { start: value.start } } } -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_from_api", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_index", issue = "143775")] impl const From> for RangeFrom { #[inline] @@ -697,7 +694,6 @@ impl const RangeBounds for RangeToInclusive<&T> { } } -// #[stable(feature = "new_range_to_inclusive_api", since = "CURRENT_RUSTC_VERSION")] #[unstable(feature = "range_into_bounds", issue = "136903")] #[rustc_const_unstable(feature = "const_range", issue = "none")] impl const IntoBounds for RangeToInclusive { @@ -706,7 +702,6 @@ impl const IntoBounds for RangeToInclusive { } } -// #[stable(feature = "new_range_to_inclusive_api", since = "CURRENT_RUSTC_VERSION")] #[unstable(feature = "one_sided_range", issue = "69780")] #[rustc_const_unstable(feature = "const_range", issue = "none")] impl const OneSidedRange for RangeToInclusive diff --git a/library/core/src/range/iter.rs b/library/core/src/range/iter.rs index 23ace6e1ba253..0cddfff4022df 100644 --- a/library/core/src/range/iter.rs +++ b/library/core/src/range/iter.rs @@ -13,6 +13,18 @@ pub struct RangeIter(legacy::Range); impl RangeIter { #[unstable(feature = "new_range_api", issue = "125687")] /// Returns the remainder of the range being iterated over. + /// + /// # Examples + /// ``` + /// #![feature(new_range_api)] + /// let range = core::range::Range::from(3..11); + /// let mut iter = range.into_iter(); + /// assert_eq!(iter.clone().remainder(), range); + /// iter.next(); + /// assert_eq!(iter.clone().remainder(), core::range::Range::from(4..11)); + /// iter.by_ref().for_each(drop); + /// assert!(iter.remainder().is_empty()); + /// ``` pub fn remainder(self) -> Range { Range { start: self.0.start, end: self.0.end } } @@ -161,6 +173,17 @@ impl RangeInclusiveIter { /// Returns the remainder of the range being iterated over. /// /// If the iterator is exhausted or empty, returns `None`. + /// + /// # Examples + /// ``` + /// let range = core::range::RangeInclusive::from(3..=11); + /// let mut iter = range.into_iter(); + /// assert_eq!(iter.clone().remainder().unwrap(), range); + /// iter.next(); + /// assert_eq!(iter.clone().remainder().unwrap(), core::range::RangeInclusive::from(4..=11)); + /// iter.by_ref().for_each(drop); + /// assert!(iter.remainder().is_none()); + /// ``` #[stable(feature = "new_range_inclusive_api", since = "1.95.0")] pub fn remainder(self) -> Option> { if self.0.is_empty() { @@ -294,7 +317,7 @@ range_incl_exact_iter_impl! { } /// By-value [`RangeFrom`] iterator. -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_from_api", since = "CURRENT_RUSTC_VERSION")] #[derive(Debug, Clone)] pub struct RangeFromIter { start: A, @@ -305,6 +328,16 @@ pub struct RangeFromIter { impl RangeFromIter { /// Returns the remainder of the range being iterated over. + /// + /// # Examples + /// ``` + /// #![feature(new_range_api)] + /// let range = core::range::RangeFrom::from(3..); + /// let mut iter = range.into_iter(); + /// assert_eq!(iter.clone().remainder(), range); + /// iter.next(); + /// assert_eq!(iter.remainder(), core::range::RangeFrom::from(4..)); + /// ``` #[inline] #[rustc_inherit_overflow_checks] #[unstable(feature = "new_range_api", issue = "125687")] @@ -321,7 +354,7 @@ impl RangeFromIter { } } -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_from_api", since = "CURRENT_RUSTC_VERSION")] impl Iterator for RangeFromIter { type Item = A; @@ -392,10 +425,10 @@ impl Iterator for RangeFromIter { #[unstable(feature = "trusted_len", issue = "37572")] unsafe impl TrustedLen for RangeFromIter {} -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_from_api", since = "CURRENT_RUSTC_VERSION")] impl FusedIterator for RangeFromIter {} -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_from_api", since = "CURRENT_RUSTC_VERSION")] impl IntoIterator for RangeFrom { type Item = A; type IntoIter = RangeFromIter; diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 1709bc7c09843..b30f82a5783ad 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -131,7 +131,7 @@ mod private_slice_index { impl Sealed for range::RangeInclusive {} #[stable(feature = "new_range_to_inclusive_api", since = "CURRENT_RUSTC_VERSION")] impl Sealed for range::RangeToInclusive {} - #[unstable(feature = "new_range_api", issue = "125687")] + #[stable(feature = "new_range_from_api", since = "CURRENT_RUSTC_VERSION")] impl Sealed for range::RangeFrom {} impl Sealed for ops::IndexRange {} @@ -588,7 +588,7 @@ unsafe impl const SliceIndex<[T]> for ops::RangeFrom { } } -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_from_api", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_index", issue = "143775")] unsafe impl const SliceIndex<[T]> for range::RangeFrom { type Output = [T]; diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs index 88a8a9764cbc5..336f074883d25 100644 --- a/library/core/src/str/traits.rs +++ b/library/core/src/str/traits.rs @@ -555,7 +555,7 @@ unsafe impl const SliceIndex for ops::RangeFrom { } } -#[unstable(feature = "new_range_api", issue = "125687")] +#[stable(feature = "new_range_from_api", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_index", issue = "143775")] unsafe impl const SliceIndex for range::RangeFrom { type Output = str; diff --git a/tests/codegen-llvm/fromrangeiter-overflow-checks.rs b/tests/codegen-llvm/fromrangeiter-overflow-checks.rs index 455c81c633407..6bc4d3de34784 100644 --- a/tests/codegen-llvm/fromrangeiter-overflow-checks.rs +++ b/tests/codegen-llvm/fromrangeiter-overflow-checks.rs @@ -11,6 +11,7 @@ #![crate_type = "lib"] #![feature(new_range_api)] + use std::range::RangeFrom; // CHECK-LABEL: @rangefrom_increments( diff --git a/tests/ui/iterators/auxiliary/rangefrom-overflow-2crates-ocno.rs b/tests/ui/iterators/auxiliary/rangefrom-overflow-2crates-ocno.rs index ebba4ae92c47f..e2aa817699a0e 100644 --- a/tests/ui/iterators/auxiliary/rangefrom-overflow-2crates-ocno.rs +++ b/tests/ui/iterators/auxiliary/rangefrom-overflow-2crates-ocno.rs @@ -1,7 +1,6 @@ //@ compile-flags: -C overflow-checks=no #![crate_type = "lib"] -#![feature(new_range_api)] use std::range::RangeFromIter; diff --git a/tests/ui/iterators/auxiliary/rangefrom-overflow-2crates-ocyes.rs b/tests/ui/iterators/auxiliary/rangefrom-overflow-2crates-ocyes.rs index 8eb5392c7bac7..b37f6985a0ea7 100644 --- a/tests/ui/iterators/auxiliary/rangefrom-overflow-2crates-ocyes.rs +++ b/tests/ui/iterators/auxiliary/rangefrom-overflow-2crates-ocyes.rs @@ -1,7 +1,6 @@ //@ compile-flags: -C overflow-checks=yes #![crate_type = "lib"] -#![feature(new_range_api)] use std::range::RangeFromIter; diff --git a/tests/ui/iterators/fromrangeiter.rs b/tests/ui/iterators/fromrangeiter.rs index 54d8f522a2c88..c672f1348437c 100644 --- a/tests/ui/iterators/fromrangeiter.rs +++ b/tests/ui/iterators/fromrangeiter.rs @@ -1,8 +1,6 @@ //@ run-pass //@ compile-flags: -C overflow-checks=yes -#![feature(new_range_api)] - use std::{iter, range}; fn main() { diff --git a/tests/ui/iterators/rangefrom-overflow-2crates.rs b/tests/ui/iterators/rangefrom-overflow-2crates.rs index c35c96f99322e..56b642b2c548e 100644 --- a/tests/ui/iterators/rangefrom-overflow-2crates.rs +++ b/tests/ui/iterators/rangefrom-overflow-2crates.rs @@ -7,8 +7,6 @@ // Test that two crates with different overflow-checks have the same results, // even when the iterator is passed between them. -#![feature(new_range_api)] - extern crate rangefrom_overflow_2crates_ocno; extern crate rangefrom_overflow_2crates_ocyes; diff --git a/tests/ui/range/new_range_stability.rs b/tests/ui/range/new_range_stability.rs index 2d129fb6815f5..0b2965ecba237 100644 --- a/tests/ui/range/new_range_stability.rs +++ b/tests/ui/range/new_range_stability.rs @@ -1,6 +1,12 @@ // Stable -use std::range::{RangeInclusive, RangeInclusiveIter, RangeToInclusive}; +use std::range::{ + RangeInclusive, + RangeInclusiveIter, + RangeToInclusive, + RangeFrom, + RangeFromIter, +}; fn range_inclusive(mut r: RangeInclusive) { &[1, 2, 3][r]; // Indexing @@ -23,15 +29,27 @@ fn range_to_inclusive(mut r: RangeToInclusive) { r.contains(&5); } +fn range_from(mut r: RangeFrom) { + &[1, 2, 3][r]; // Indexing + + r.start; + r.contains(&5); + r.iter(); + + let mut i = r.into_iter(); + i.next(); + + // Left unstable + i.remainder(); //~ ERROR unstable +} + // Unstable module use std::range::legacy; //~ ERROR unstable // Unstable types -use std::range::RangeFrom; //~ ERROR unstable use std::range::Range; //~ ERROR unstable -use std::range::RangeFromIter; //~ ERROR unstable use std::range::RangeIter; //~ ERROR unstable fn main() {} diff --git a/tests/ui/range/new_range_stability.stderr b/tests/ui/range/new_range_stability.stderr index b5a7e06e5f2ea..747c39d001eb4 100644 --- a/tests/ui/range/new_range_stability.stderr +++ b/tests/ui/range/new_range_stability.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature `new_range_api` - --> $DIR/new_range_stability.rs:28:5 + --> $DIR/new_range_stability.rs:48:5 | LL | use std::range::legacy; | ^^^^^^^^^^^^^^^^^^ @@ -9,17 +9,7 @@ LL | use std::range::legacy; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `new_range_api` - --> $DIR/new_range_stability.rs:32:5 - | -LL | use std::range::RangeFrom; - | ^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #125687 for more information - = help: add `#![feature(new_range_api)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: use of unstable library feature `new_range_api` - --> $DIR/new_range_stability.rs:33:5 + --> $DIR/new_range_stability.rs:52:5 | LL | use std::range::Range; | ^^^^^^^^^^^^^^^^^ @@ -29,25 +19,25 @@ LL | use std::range::Range; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `new_range_api` - --> $DIR/new_range_stability.rs:34:5 + --> $DIR/new_range_stability.rs:53:5 | -LL | use std::range::RangeFromIter; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | use std::range::RangeIter; + | ^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #125687 for more information = help: add `#![feature(new_range_api)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: use of unstable library feature `new_range_api` - --> $DIR/new_range_stability.rs:35:5 + --> $DIR/new_range_stability.rs:43:7 | -LL | use std::range::RangeIter; - | ^^^^^^^^^^^^^^^^^^^^^ +LL | i.remainder(); + | ^^^^^^^^^ | = note: see issue #125687 for more information = help: add `#![feature(new_range_api)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0658`.