|
| 1 | +- Feature Name: panic_safe_slicing |
| 2 | +- Start Date: 2015-10-16 |
| 3 | +- RFC PR: (leave this empty) |
| 4 | +- Rust Issue: (leave this empty) |
| 5 | + |
| 6 | +# Summary |
| 7 | + |
| 8 | +Add "panic-safe" or "total" alternatives to the existing panicking slicing syntax. |
| 9 | + |
| 10 | +# Motivation |
| 11 | + |
| 12 | +`SliceExt::get` and `SliceExt::get_mut` can be thought as non-panicking versions of the simple |
| 13 | +slicing syntax, `a[idx]`. However, there is no such equivalent for `a[start..end]`, `a[start..]`, |
| 14 | +or `a[..end]`. This RFC proposes such methods to fill the gap. |
| 15 | + |
| 16 | +# Detailed design |
| 17 | + |
| 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 | + |
| 22 | +```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 | +} |
| 31 | + |
| 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() } |
| 35 | +} |
| 36 | + |
| 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 } |
| 40 | +} |
| 41 | + |
| 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 } |
| 45 | +} |
| 46 | + |
| 47 | +trait GetRangeExt: SliceExt { |
| 48 | + fn get_range<R: Rangeable<Self>>(&self, range: R) -> Option<&[Self::Item]>; |
| 49 | +} |
| 50 | + |
| 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); |
| 55 | + |
| 56 | + if start > end { return None; } |
| 57 | + if end > self.len() { return None; } |
| 58 | + |
| 59 | + unsafe { Some(from_raw_parts(self.as_ptr().offset(start as isize), end - start)) } |
| 60 | + } |
| 61 | +} |
| 62 | + |
| 63 | +fn main() { |
| 64 | + let a = [1, 2, 3, 4, 5]; |
| 65 | + |
| 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); |
| 71 | +} |
| 72 | +``` |
| 73 | + |
| 74 | +`get_range_unchecked` and `get_range_unchecked_mut` should be the unchecked versions of the methods |
| 75 | +above. |
| 76 | + |
| 77 | +# Drawbacks |
| 78 | + |
| 79 | +- Are these methods worth adding to `std`? Are such use cases common to justify such extention? |
| 80 | + |
| 81 | +# Alternatives |
| 82 | + |
| 83 | +- Stay as is. |
| 84 | +- Could there be any other (and better!) total functions that serve the similar purpose? |
| 85 | + |
| 86 | +# Unresolved questions |
| 87 | + |
| 88 | +- Naming, naming, naming: Is `get_range` the most suitable name? How about `get_slice`, or just |
| 89 | + `slice`? Or any others? |
0 commit comments