Skip to content

Commit 12071f3

Browse files
committed
Rewrite to overload existing methods.
1 parent be5eb6f commit 12071f3

File tree

1 file changed

+60
-45
lines changed

1 file changed

+60
-45
lines changed

text/0000-panic-safe-slicing.md

Lines changed: 60 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -15,75 +15,90 @@ or `a[..end]`. This RFC proposes such methods to fill the gap.
1515

1616
# Detailed design
1717

18-
Add `get_range`, `get_range_mut`, `get_range_unchecked`, `get_range_unchecked_mut` to `SliceExt`.
19-
20-
`get_range` and `get_range_mut` may be implemented roughly as follows:
21-
18+
Introduce a `SliceIndex` trait which is implemented by types which can index into a slice:
2219
```rust
23-
use std::ops::{RangeFrom, RangeTo, Range};
24-
use std::slice::from_raw_parts;
25-
use core::slice::SliceExt;
26-
27-
trait Rangeable<T: ?Sized> {
28-
fn start(&self, slice: &T) -> usize;
29-
fn end(&self, slice: &T) -> usize;
30-
}
20+
pub trait SliceIndex<T> {
21+
type Output: ?Sized;
3122

32-
impl<T: SliceExt + ?Sized> Rangeable<T> for RangeFrom<usize> {
33-
fn start(&self, _: &T) -> usize { self.start }
34-
fn end(&self, slice: &T) -> usize { slice.len() }
23+
fn get(self, slice: &[T]) -> Option<&Self::Output>;
24+
fn get_mut(self, slice: &mut [T]) -> Option<&mut Self::Output>;
25+
unsafe fn get_unchecked(self, slice: &[T]) -> &Self::Output;
26+
unsafe fn get_mut_unchecked(self, slice: &[T]) -> &mut Self::Output;
3527
}
3628

37-
impl<T: SliceExt + ?Sized> Rangeable<T> for RangeTo<usize> {
38-
fn start(&self, _: &T) -> usize { 0 }
39-
fn end(&self, _: &T) -> usize { self.end }
29+
impl<T> SliceIndex<T> for usize {
30+
type Output = T;
31+
// ...
4032
}
4133

42-
impl<T: SliceExt + ?Sized> Rangeable<T> for Range<usize> {
43-
fn start(&self, _: &T) -> usize { self.start }
44-
fn end(&self, _: &T) -> usize { self.end }
34+
impl<T, R> SliceIndex<T> for R
35+
where R: RangeArgument<usize>
36+
{
37+
type Output = [T];
38+
// ...
4539
}
40+
```
4641

47-
trait GetRangeExt: SliceExt {
48-
fn get_range<R: Rangeable<Self>>(&self, range: R) -> Option<&[Self::Item]>;
49-
}
42+
Alter the `Index`, `IndexMut`, `get`, `get_mut`, `get_unchecked`, and `get_mut_unchecked`
43+
implementations to be generic over `SliceIndex`:
44+
```rust
45+
impl<T> [T] {
46+
pub fn get<I>(&self, idx: I) -> Option<I::Output>
47+
where I: SliceIndex<T>
48+
{
49+
idx.get(self)
50+
}
5051

51-
impl<T> GetRangeExt for [T] {
52-
fn get_range<R: Rangeable<Self>>(&self, range: R) -> Option<&[T]> {
53-
let start = range.start(self);
54-
let end = range.end(self);
52+
pub fn get_mut<I>(&mut self, idx: I) -> Option<I::Output>
53+
where I: SliceIndex<T>
54+
{
55+
idx.get_mut(self)
56+
}
5557

56-
if start > end { return None; }
57-
if end > self.len() { return None; }
58+
pub unsafe fn get_unchecked<I>(&self, idx: I) -> I::Output
59+
where I: SliceIndex<T>
60+
{
61+
idx.get_unchecked(self)
62+
}
5863

59-
unsafe { Some(from_raw_parts(self.as_ptr().offset(start as isize), end - start)) }
64+
pub unsafe fn get_mut_unchecked<I>(&mut self, idx: I) -> I::Output
65+
where I: SliceIndex<T>
66+
{
67+
idx.get_mut_unchecked(self)
6068
}
6169
}
6270

63-
fn main() {
64-
let a = [1, 2, 3, 4, 5];
71+
impl<T, I> Index<I> for [T]
72+
where I: SliceIndex<T>
73+
{
74+
type Output = I::Output;
6575

66-
assert_eq!(a.get_range(1..), Some(&a[1..]));
67-
assert_eq!(a.get_range(..3), Some(&a[..3]));
68-
assert_eq!(a.get_range(2..5), Some(&a[2..5]));
69-
assert_eq!(a.get_range(..6), None);
70-
assert_eq!(a.get_range(4..2), None);
76+
fn index(&self, idx: I) -> &I::Output {
77+
self.get(idx).expect("out of bounds slice access")
78+
}
7179
}
72-
```
7380

74-
`get_range_unchecked` and `get_range_unchecked_mut` should be the unchecked versions of the methods
75-
above.
81+
impl<T, I> IndexMut<I> for [T]
82+
where I: SliceIndex<T>
83+
{
84+
fn index_mut(&self, idx: I) -> &mut I::Output {
85+
self.get_mut(idx).expect("out of bounds slice access")
86+
}
87+
}
88+
```
7689

7790
# Drawbacks
7891

79-
- Are these methods worth adding to `std`? Are such use cases common to justify such extention?
92+
- The `SliceIndex` trait is unfortunate - it's tuned for exactly the set of methods it's used by.
93+
It only exists because inherent methods cannot be overloaded the same way that trait
94+
implementations can be. It would most likely remain unstable indefinitely.
8095

8196
# Alternatives
8297

8398
- Stay as is.
84-
- Could there be any other (and better!) total functions that serve the similar purpose?
99+
- A previous version of this RFC introduced new `get_slice` etc methods rather than overloading
100+
`get` etc. This avoids the utility trait but is somewhat less ergonomic.
85101

86102
# Unresolved questions
87103

88-
- Naming, naming, naming: Is `get_range` the most suitable name? How about `get_slice`, or just
89-
`slice`? Or any others?
104+
None

0 commit comments

Comments
 (0)