Rollup merge of #150522 - pitaj:stabilize-new-rangeinclusive, r=tgross35

Stabilize new inclusive range type and iterator type

Part 1 of stabilizing the new range types for rust-lang/rust#125687

stabilizes `core::range::RangeInclusive` and `core::range::RangeInclusiveIter`. Newly stable API:

```rust
// in core and std
pub mod range;

// in core::range

pub struct RangeInclusive<Idx> {
    pub start: Idx,
    pub last: Idx,
}

impl<Idx: fmt::Debug> fmt::Debug for RangeInclusive<Idx> { /* ... */ }

impl<Idx: PartialOrd<Idx>> RangeInclusive<Idx> {
    pub const fn contains<U>(&self, item: &U) -> bool
    where
        Idx: [const] PartialOrd<U>,
        U: ?Sized + [const] PartialOrd<Idx>;

    pub const fn is_empty(&self) -> bool
    where
        Idx: [const] PartialOrd;
}

impl<Idx: Step> RangeInclusive<Idx> {
    pub fn iter(&self) -> RangeInclusiveIter<Idx>;
}

impl<T> const RangeBounds<T> for RangeInclusive<T> { /* ... */ }
impl<T> const RangeBounds<T> for RangeInclusive<&T> { /* ... */ }

impl<T> const From<RangeInclusive<T>> for legacy::RangeInclusive<T> { /* ... */ }
impl<T> const From<legacy::RangeInclusive<T>> for RangeInclusive<T> { /* ... */ }

pub struct RangeInclusiveIter<A>(/* ... */);

impl<A: Step> RangeInclusiveIter<A> {
    pub fn remainder(self) -> Option<RangeInclusive<A>>;
}

impl<A: Step> Iterator for RangeInclusiveIter<A> {
    type Item = A;
    /* ... */
}

impl<A: Step> DoubleEndedIterator for RangeInclusiveIter<A> { /* ... */ }
impl<A: Step> FusedIterator for RangeInclusiveIter<A> { }
impl<A: Step> IntoIterator for RangeInclusive<A> {
    type Item = A;
    type IntoIter = RangeInclusiveIter<A>;
    /* ... */
}

impl ExactSizeIterator for RangeInclusiveIter<u8> { }
impl ExactSizeIterator for RangeInclusiveIter<i8> { }

unsafe impl<T> const SliceIndex<[T]> for range::RangeInclusive<usize> {
    type Output = [T];
    /* ... */
}
unsafe impl const SliceIndex<str> for range::RangeInclusive<usize> {
    type Output = str;
    /* ... */
}
```

I've removed the re-exports temporarily because from what I can tell, there's no way to make re-exports of stable items unstable. They will be added back and stabilized in a separate PR.
This commit is contained in:
Jonathan Brouwer 2026-02-07 13:06:34 +01:00 committed by GitHub
commit 29079e41a7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 140 additions and 46 deletions

View file

@ -315,7 +315,7 @@ unsafe impl<T> SliceIndex<[T]> for Clamp<ops::RangeFrom<usize>> {
}
#[unstable(feature = "sliceindex_wrappers", issue = "146179")]
unsafe impl<T> SliceIndex<[T]> for Clamp<range::RangeTo<usize>> {
unsafe impl<T> SliceIndex<[T]> for Clamp<ops::RangeTo<usize>> {
type Output = [T];
fn get(self, slice: &[T]) -> Option<&Self::Output> {
@ -408,7 +408,7 @@ unsafe impl<T> SliceIndex<[T]> for Clamp<ops::RangeToInclusive<usize>> {
}
#[unstable(feature = "sliceindex_wrappers", issue = "146179")]
unsafe impl<T> SliceIndex<[T]> for Clamp<range::RangeFull> {
unsafe impl<T> SliceIndex<[T]> for Clamp<ops::RangeFull> {
type Output = [T];
fn get(self, slice: &[T]) -> Option<&Self::Output> {

View file

@ -324,7 +324,7 @@ pub mod pat;
pub mod pin;
#[unstable(feature = "random", issue = "130703")]
pub mod random;
#[unstable(feature = "new_range_api", issue = "125687")]
#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
pub mod range;
pub mod result;
pub mod sync;

View file

@ -1,6 +1,6 @@
//! Random value generation.
use crate::range::RangeFull;
use crate::ops::RangeFull;
/// A source of randomness.
#[unstable(feature = "random", issue = "130703")]

View file

@ -24,14 +24,26 @@ mod iter;
#[unstable(feature = "new_range_api", issue = "125687")]
pub mod legacy;
use Bound::{Excluded, Included, Unbounded};
#[doc(inline)]
pub use iter::{RangeFromIter, RangeInclusiveIter, RangeIter};
#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
pub use iter::RangeInclusiveIter;
#[doc(inline)]
#[unstable(feature = "new_range_api", issue = "125687")]
pub use iter::{RangeFromIter, RangeIter};
#[doc(inline)]
pub use crate::iter::Step;
#[doc(inline)]
pub use crate::ops::{Bound, IntoBounds, OneSidedRange, RangeBounds, RangeFull, RangeTo};
// FIXME(#125687): re-exports temporarily removed
// Because re-exports of stable items (Bound, RangeBounds, RangeFull, RangeTo)
// can't be made unstable.
//
// #[doc(inline)]
// #[unstable(feature = "new_range_api", issue = "125687")]
// pub use crate::iter::Step;
// #[doc(inline)]
// #[unstable(feature = "new_range_api", issue = "125687")]
// pub use crate::ops::{Bound, IntoBounds, OneSidedRange, RangeBounds, RangeFull, RangeTo};
use crate::iter::Step;
use crate::ops::Bound::{self, Excluded, Included, Unbounded};
use crate::ops::{IntoBounds, RangeBounds};
/// A (half-open) range bounded inclusively below and exclusively above
/// (`start..end` in a future edition).
@ -226,7 +238,6 @@ impl<T> const From<legacy::Range<T>> for Range<T> {
/// The `start..=last` syntax is a `RangeInclusive`:
///
/// ```
/// #![feature(new_range_api)]
/// use core::range::RangeInclusive;
///
/// assert_eq!(RangeInclusive::from(3..=5), RangeInclusive { start: 3, last: 5 });
@ -234,17 +245,17 @@ impl<T> const From<legacy::Range<T>> for Range<T> {
/// ```
#[lang = "RangeInclusiveCopy"]
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[unstable(feature = "new_range_api", issue = "125687")]
#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
pub struct RangeInclusive<Idx> {
/// The lower bound of the range (inclusive).
#[unstable(feature = "new_range_api", issue = "125687")]
#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
pub start: Idx,
/// The upper bound of the range (inclusive).
#[unstable(feature = "new_range_api", issue = "125687")]
#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
pub last: Idx,
}
#[unstable(feature = "new_range_api", issue = "125687")]
#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
impl<Idx: fmt::Debug> fmt::Debug for RangeInclusive<Idx> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
self.start.fmt(fmt)?;
@ -260,7 +271,6 @@ impl<Idx: PartialOrd<Idx>> RangeInclusive<Idx> {
/// # Examples
///
/// ```
/// #![feature(new_range_api)]
/// use core::range::RangeInclusive;
///
/// assert!(!RangeInclusive::from(3..=5).contains(&2));
@ -278,7 +288,7 @@ impl<Idx: PartialOrd<Idx>> RangeInclusive<Idx> {
/// assert!(!RangeInclusive::from(f32::NAN..=1.0).contains(&1.0));
/// ```
#[inline]
#[unstable(feature = "new_range_api", issue = "125687")]
#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_unstable(feature = "const_range", issue = "none")]
pub const fn contains<U>(&self, item: &U) -> bool
where
@ -293,7 +303,6 @@ impl<Idx: PartialOrd<Idx>> RangeInclusive<Idx> {
/// # Examples
///
/// ```
/// #![feature(new_range_api)]
/// use core::range::RangeInclusive;
///
/// assert!(!RangeInclusive::from(3..=5).is_empty());
@ -304,14 +313,13 @@ impl<Idx: PartialOrd<Idx>> RangeInclusive<Idx> {
/// The range is empty if either side is incomparable:
///
/// ```
/// #![feature(new_range_api)]
/// use core::range::RangeInclusive;
///
/// assert!(!RangeInclusive::from(3.0..=5.0).is_empty());
/// assert!( RangeInclusive::from(3.0..=f32::NAN).is_empty());
/// assert!( RangeInclusive::from(f32::NAN..=5.0).is_empty());
/// ```
#[unstable(feature = "new_range_api", issue = "125687")]
#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
#[inline]
#[rustc_const_unstable(feature = "const_range", issue = "none")]
pub const fn is_empty(&self) -> bool
@ -330,7 +338,6 @@ impl<Idx: Step> RangeInclusive<Idx> {
/// # Examples
///
/// ```
/// #![feature(new_range_api)]
/// use core::range::RangeInclusive;
///
/// let mut i = RangeInclusive::from(3..=8).iter().map(|n| n*n);
@ -338,7 +345,7 @@ impl<Idx: Step> RangeInclusive<Idx> {
/// assert_eq!(i.next(), Some(16));
/// assert_eq!(i.next(), Some(25));
/// ```
#[unstable(feature = "new_range_api", issue = "125687")]
#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
#[inline]
pub fn iter(&self) -> RangeInclusiveIter<Idx> {
self.clone().into_iter()
@ -354,7 +361,7 @@ impl RangeInclusive<usize> {
}
}
#[unstable(feature = "new_range_api", issue = "125687")]
#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_unstable(feature = "const_range", issue = "none")]
impl<T> const RangeBounds<T> for RangeInclusive<T> {
fn start_bound(&self) -> Bound<&T> {
@ -371,7 +378,7 @@ impl<T> const RangeBounds<T> for RangeInclusive<T> {
/// 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..=end` with `(Bound::Included(start), Bound::Included(end))`.
#[unstable(feature = "new_range_api", issue = "125687")]
#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_unstable(feature = "const_range", issue = "none")]
impl<T> const RangeBounds<T> for RangeInclusive<&T> {
fn start_bound(&self) -> Bound<&T> {
@ -382,8 +389,8 @@ impl<T> const RangeBounds<T> for RangeInclusive<&T> {
}
}
// #[unstable(feature = "range_into_bounds", issue = "136903")]
#[unstable(feature = "new_range_api", issue = "125687")]
// #[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
#[unstable(feature = "range_into_bounds", issue = "136903")]
#[rustc_const_unstable(feature = "const_range", issue = "none")]
impl<T> const IntoBounds<T> for RangeInclusive<T> {
fn into_bounds(self) -> (Bound<T>, Bound<T>) {
@ -391,7 +398,7 @@ impl<T> const IntoBounds<T> for RangeInclusive<T> {
}
}
#[unstable(feature = "new_range_api", issue = "125687")]
#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
impl<T> const From<RangeInclusive<T>> for legacy::RangeInclusive<T> {
#[inline]
@ -399,7 +406,7 @@ impl<T> const From<RangeInclusive<T>> for legacy::RangeInclusive<T> {
Self::new(value.start, value.last)
}
}
#[unstable(feature = "new_range_api", issue = "125687")]
#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_unstable(feature = "const_convert", issue = "143773")]
impl<T> const From<legacy::RangeInclusive<T>> for RangeInclusive<T> {
#[inline]
@ -650,12 +657,13 @@ impl<Idx: PartialOrd<Idx>> RangeToInclusive<Idx> {
}
}
#[unstable(feature = "new_range_api", issue = "125687")]
impl<T> From<legacy::RangeToInclusive<T>> for RangeToInclusive<T> {
fn from(value: legacy::RangeToInclusive<T>) -> Self {
Self { last: value.end }
}
}
#[unstable(feature = "new_range_api", issue = "125687")]
impl<T> From<RangeToInclusive<T>> for legacy::RangeToInclusive<T> {
fn from(value: RangeToInclusive<T>) -> Self {
Self { end: value.last }

View file

@ -11,6 +11,7 @@ use crate::{intrinsics, mem};
pub struct RangeIter<A>(legacy::Range<A>);
impl<A> RangeIter<A> {
#[unstable(feature = "new_range_api", issue = "125687")]
/// Returns the remainder of the range being iterated over.
pub fn remainder(self) -> Range<A> {
Range { start: self.0.start, end: self.0.end }
@ -152,7 +153,7 @@ impl<A: Step> IntoIterator for Range<A> {
}
/// By-value [`RangeInclusive`] iterator.
#[unstable(feature = "new_range_api", issue = "125687")]
#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
#[derive(Debug, Clone)]
pub struct RangeInclusiveIter<A>(legacy::RangeInclusive<A>);
@ -160,6 +161,7 @@ impl<A: Step> RangeInclusiveIter<A> {
/// Returns the remainder of the range being iterated over.
///
/// If the iterator is exhausted or empty, returns `None`.
#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
pub fn remainder(self) -> Option<RangeInclusive<A>> {
if self.0.is_empty() {
return None;
@ -169,7 +171,7 @@ impl<A: Step> RangeInclusiveIter<A> {
}
}
#[unstable(feature = "new_range_api", issue = "125687")]
#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
impl<A: Step> Iterator for RangeInclusiveIter<A> {
type Item = A;
@ -225,7 +227,7 @@ impl<A: Step> Iterator for RangeInclusiveIter<A> {
}
}
#[unstable(feature = "new_range_api", issue = "125687")]
#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
impl<A: Step> DoubleEndedIterator for RangeInclusiveIter<A> {
#[inline]
fn next_back(&mut self) -> Option<A> {
@ -246,10 +248,10 @@ impl<A: Step> DoubleEndedIterator for RangeInclusiveIter<A> {
#[unstable(feature = "trusted_len", issue = "37572")]
unsafe impl<A: TrustedStep> TrustedLen for RangeInclusiveIter<A> {}
#[unstable(feature = "new_range_api", issue = "125687")]
#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
impl<A: Step> FusedIterator for RangeInclusiveIter<A> {}
#[unstable(feature = "new_range_api", issue = "125687")]
#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
impl<A: Step> IntoIterator for RangeInclusive<A> {
type Item = A;
type IntoIter = RangeInclusiveIter<A>;
@ -276,7 +278,7 @@ macro_rules! range_exact_iter_impl {
macro_rules! range_incl_exact_iter_impl {
($($t:ty)*) => ($(
#[unstable(feature = "new_range_api", issue = "125687")]
#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
impl ExactSizeIterator for RangeInclusiveIter<$t> { }
)*)
}
@ -305,6 +307,7 @@ impl<A: Step> RangeFromIter<A> {
/// Returns the remainder of the range being iterated over.
#[inline]
#[rustc_inherit_overflow_checks]
#[unstable(feature = "new_range_api", issue = "125687")]
pub fn remainder(self) -> RangeFrom<A> {
if intrinsics::overflow_checks() {
if !self.first {

View file

@ -127,7 +127,7 @@ mod private_slice_index {
#[unstable(feature = "new_range_api", issue = "125687")]
impl Sealed for range::Range<usize> {}
#[unstable(feature = "new_range_api", issue = "125687")]
#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
impl Sealed for range::RangeInclusive<usize> {}
#[unstable(feature = "new_range_api", issue = "125687")]
impl Sealed for range::RangeToInclusive<usize> {}
@ -724,7 +724,7 @@ unsafe impl<T> const SliceIndex<[T]> for ops::RangeInclusive<usize> {
}
}
#[unstable(feature = "new_range_api", issue = "125687")]
#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
unsafe impl<T> const SliceIndex<[T]> for range::RangeInclusive<usize> {
type Output = [T];

View file

@ -672,7 +672,7 @@ unsafe impl const SliceIndex<str> for ops::RangeInclusive<usize> {
}
}
#[unstable(feature = "new_range_api", issue = "125687")]
#[stable(feature = "new_range_inclusive_api", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_unstable(feature = "const_index", issue = "143775")]
unsafe impl const SliceIndex<str> for range::RangeInclusive<usize> {
type Output = str;

View file

@ -4,11 +4,12 @@
fn main() {
// Unchanged
let a: core::range::RangeFull = ..;
let b: core::range::RangeTo<u8> = ..2;
let a: core::ops::RangeFull = ..;
let b: core::ops::RangeTo<u8> = ..2;
let _: core::ops::RangeFull = a;
let _: core::ops::RangeTo<u8> = b;
// FIXME(#125687): re-exports temporarily removed
// let _: core::range::RangeFull = a;
// let _: core::range::RangeTo<u8> = b;
// Changed
let a: core::range::legacy::RangeFrom<u8> = 1..;

View file

@ -5,11 +5,12 @@
fn main() {
// Unchanged
let a: core::range::RangeFull = ..;
let b: core::range::RangeTo<u8> = ..2;
let a: core::ops::RangeFull = ..;
let b: core::ops::RangeTo<u8> = ..2;
let _: core::ops::RangeFull = a;
let _: core::ops::RangeTo<u8> = b;
// FIXME(#125687): re-exports temporarily removed
// let _: core::range::RangeFull = a;
// let _: core::range::RangeTo<u8> = b;
// Changed
let a: core::range::RangeFrom<u8> = 1..;

View file

@ -0,0 +1,28 @@
// Stable
use std::range::{RangeInclusive, RangeInclusiveIter};
fn range_inclusive(mut r: RangeInclusive<usize>) {
r.start;
r.last;
r.contains(&5);
r.is_empty();
r.iter();
let mut i = r.into_iter();
i.next();
i.remainder();
}
// 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() {}

View file

@ -0,0 +1,53 @@
error[E0658]: use of unstable library feature `new_range_api`
--> $DIR/new_range_stability.rs:19:5
|
LL | use std::range::legacy;
| ^^^^^^^^^^^^^^^^^^
|
= note: see issue #125687 <https://github.com/rust-lang/rust/issues/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:23:5
|
LL | use std::range::RangeFrom;
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #125687 <https://github.com/rust-lang/rust/issues/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:24:5
|
LL | use std::range::Range;
| ^^^^^^^^^^^^^^^^^
|
= note: see issue #125687 <https://github.com/rust-lang/rust/issues/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:25:5
|
LL | use std::range::RangeFromIter;
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #125687 <https://github.com/rust-lang/rust/issues/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:26:5
|
LL | use std::range::RangeIter;
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #125687 <https://github.com/rust-lang/rust/issues/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
For more information about this error, try `rustc --explain E0658`.