about summary refs log tree commit diff
path: root/library/core
diff options
context:
space:
mode:
authorTaylor Cramer <cramertj@google.com>2025-02-04 11:42:29 -0800
committerTaylor Cramer <cramertj@google.com>2025-02-04 11:45:21 -0800
commit836a989820aa82a7bb35c9fba670ab733406a84f (patch)
tree59c99365dfc7c08fda198cac67abb9ca0bc64bef /library/core
parent07cf20e98773677746d09f10ff4bdae8b7ee22c6 (diff)
downloadrust-836a989820aa82a7bb35c9fba670ab733406a84f.tar.gz
rust-836a989820aa82a7bb35c9fba670ab733406a84f.zip
Add OneSidedRangeBound to eliminate panic in `split_point_of`
See discussion in https://github.com/rust-lang/rust/pull/88502/files#r760177240
Diffstat (limited to 'library/core')
-rw-r--r--library/core/src/ops/mod.rs4
-rw-r--r--library/core/src/ops/range.rs46
-rw-r--r--library/core/src/slice/mod.rs16
3 files changed, 51 insertions, 15 deletions
diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs
index 40526f9583e..7b2ced2cc4b 100644
--- a/library/core/src/ops/mod.rs
+++ b/library/core/src/ops/mod.rs
@@ -182,10 +182,10 @@ pub use self::function::{Fn, FnMut, FnOnce};
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::index::{Index, IndexMut};
 pub(crate) use self::index_range::IndexRange;
-#[unstable(feature = "one_sided_range", issue = "69780")]
-pub use self::range::OneSidedRange;
 #[stable(feature = "inclusive_range", since = "1.26.0")]
 pub use self::range::{Bound, RangeBounds, RangeInclusive, RangeToInclusive};
+#[unstable(feature = "one_sided_range", issue = "69780")]
+pub use self::range::{OneSidedRange, OneSidedRangeBound};
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::range::{Range, RangeFrom, RangeFull, RangeTo};
 #[unstable(feature = "try_trait_v2_residual", issue = "91285")]
diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs
index 727a22e454d..42e07a0e51d 100644
--- a/library/core/src/ops/range.rs
+++ b/library/core/src/ops/range.rs
@@ -979,6 +979,19 @@ impl<T> RangeBounds<T> for RangeToInclusive<&T> {
     }
 }
 
+/// An internal helper for `split_off` functions indicating
+/// which end a `OneSidedRange` is bounded on.
+#[unstable(feature = "one_sided_range", issue = "69780")]
+#[allow(missing_debug_implementations)]
+pub enum OneSidedRangeBound {
+    /// The range is bounded inclusively from below and is unbounded above.
+    StartInclusive,
+    /// The range is bounded exclusively from above and is unbounded below.
+    End,
+    /// The range is bounded inclusively from above and is unbounded below.
+    EndInclusive,
+}
+
 /// `OneSidedRange` is implemented for built-in range types that are unbounded
 /// on one side. For example, `a..`, `..b` and `..=c` implement `OneSidedRange`,
 /// but `..`, `d..e`, and `f..=g` do not.
@@ -986,13 +999,38 @@ impl<T> RangeBounds<T> for RangeToInclusive<&T> {
 /// Types that implement `OneSidedRange<T>` must return `Bound::Unbounded`
 /// from one of `RangeBounds::start_bound` or `RangeBounds::end_bound`.
 #[unstable(feature = "one_sided_range", issue = "69780")]
-pub trait OneSidedRange<T: ?Sized>: RangeBounds<T> {}
+pub trait OneSidedRange<T: ?Sized>: RangeBounds<T> {
+    /// An internal-only helper function for `split_off` and
+    /// `split_off_mut` that returns the bound of the one-sided range.
+    fn bound(self) -> (OneSidedRangeBound, T);
+}
 
 #[unstable(feature = "one_sided_range", issue = "69780")]
-impl<T> OneSidedRange<T> for RangeTo<T> where Self: RangeBounds<T> {}
+impl<T> OneSidedRange<T> for RangeTo<T>
+where
+    Self: RangeBounds<T>,
+{
+    fn bound(self) -> (OneSidedRangeBound, T) {
+        (OneSidedRangeBound::End, self.end)
+    }
+}
 
 #[unstable(feature = "one_sided_range", issue = "69780")]
-impl<T> OneSidedRange<T> for RangeFrom<T> where Self: RangeBounds<T> {}
+impl<T> OneSidedRange<T> for RangeFrom<T>
+where
+    Self: RangeBounds<T>,
+{
+    fn bound(self) -> (OneSidedRangeBound, T) {
+        (OneSidedRangeBound::StartInclusive, self.start)
+    }
+}
 
 #[unstable(feature = "one_sided_range", issue = "69780")]
-impl<T> OneSidedRange<T> for RangeToInclusive<T> where Self: RangeBounds<T> {}
+impl<T> OneSidedRange<T> for RangeToInclusive<T>
+where
+    Self: RangeBounds<T>,
+{
+    fn bound(self) -> (OneSidedRangeBound, T) {
+        (OneSidedRangeBound::EndInclusive, self.end)
+    }
+}
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index dd2b2fd15f9..fe9d7c10db2 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -10,7 +10,7 @@ use crate::cmp::Ordering::{self, Equal, Greater, Less};
 use crate::intrinsics::{exact_div, unchecked_sub};
 use crate::mem::{self, SizedTypeProperties};
 use crate::num::NonZero;
-use crate::ops::{Bound, OneSidedRange, Range, RangeBounds, RangeInclusive};
+use crate::ops::{OneSidedRange, OneSidedRangeBound, Range, RangeBounds, RangeInclusive};
 use crate::panic::const_panic;
 use crate::simd::{self, Simd};
 use crate::ub_checks::assert_unsafe_precondition;
@@ -83,14 +83,12 @@ pub use raw::{from_raw_parts, from_raw_parts_mut};
 /// which to split. Returns `None` if the split index would overflow.
 #[inline]
 fn split_point_of(range: impl OneSidedRange<usize>) -> Option<(Direction, usize)> {
-    use Bound::*;
-
-    Some(match (range.start_bound(), range.end_bound()) {
-        (Unbounded, Excluded(i)) => (Direction::Front, *i),
-        (Unbounded, Included(i)) => (Direction::Front, i.checked_add(1)?),
-        (Excluded(i), Unbounded) => (Direction::Back, i.checked_add(1)?),
-        (Included(i), Unbounded) => (Direction::Back, *i),
-        _ => unreachable!(),
+    use OneSidedRangeBound::{End, EndInclusive, StartInclusive};
+
+    Some(match range.bound() {
+        (StartInclusive, i) => (Direction::Back, i),
+        (End, i) => (Direction::Front, i),
+        (EndInclusive, i) => (Direction::Front, i.checked_add(1)?),
     })
 }