Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add panic-safe slicing methods #1325

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions text/0000-panic-safe-slicing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
- Feature Name: panic_safe_slicing
- Start Date: 2015-10-16
- RFC PR: (leave this empty)
- Rust Issue: (leave this empty)

# Summary

Add "panic-safe" or "total" alternatives to the existing panicking slicing syntax.

# Motivation

`SliceExt::get` and `SliceExt::get_mut` can be thought as non-panicking versions of the simple
slicing syntax, `a[idx]`. However, there is no such equivalent for `a[start..end]`, `a[start..]`,
or `a[..end]`. This RFC proposes such methods to fill the gap.

# Detailed design

Add `get_range`, `get_range_mut`, `get_range_unchecked`, `get_range_unchecked_mut` to `SliceExt`.

`get_range` and `get_range_mut` may be implemented roughly as follows:

```rust
use std::ops::{RangeFrom, RangeTo, Range};
use std::slice::from_raw_parts;
use core::slice::SliceExt;

trait Rangeable<T: ?Sized> {
fn start(&self, slice: &T) -> usize;
fn end(&self, slice: &T) -> usize;
}

impl<T: SliceExt + ?Sized> Rangeable<T> for RangeFrom<usize> {
fn start(&self, _: &T) -> usize { self.start }
fn end(&self, slice: &T) -> usize { slice.len() }
}

impl<T: SliceExt + ?Sized> Rangeable<T> for RangeTo<usize> {
fn start(&self, _: &T) -> usize { 0 }
fn end(&self, _: &T) -> usize { self.end }
}

impl<T: SliceExt + ?Sized> Rangeable<T> for Range<usize> {
fn start(&self, _: &T) -> usize { self.start }
fn end(&self, _: &T) -> usize { self.end }
}

trait GetRangeExt: SliceExt {
fn get_range<R: Rangeable<Self>>(&self, range: R) -> Option<&[Self::Item]>;
}

impl<T> GetRangeExt for [T] {
fn get_range<R: Rangeable<Self>>(&self, range: R) -> Option<&[T]> {
let start = range.start(self);
let end = range.end(self);

if start > end { return None; }
if end > self.len() { return None; }

unsafe { Some(from_raw_parts(self.as_ptr().offset(start as isize), end - start)) }
}
}

fn main() {
let a = [1, 2, 3, 4, 5];

assert_eq!(a.get_range(1..), Some(&a[1..]));
assert_eq!(a.get_range(..3), Some(&a[..3]));
assert_eq!(a.get_range(2..5), Some(&a[2..5]));
assert_eq!(a.get_range(..6), None);
assert_eq!(a.get_range(4..2), None);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alternative would be:

assert_eq!(a.get_range(1..), &a[1..]);
assert_eq!(a.get_range(..3), &a[..3]);
assert_eq!(a.get_range(2..5), &a[2..5]);
assert_eq!(a.get_range(..6), &[]);
assert_eq!(a.get_range(4..2), &[]);

}
```

`get_range_unchecked` and `get_range_unchecked_mut` should be the unchecked versions of the methods
above.

# Drawbacks

- Are these methods worth adding to `std`? Are such use cases common to justify such extention?

# Alternatives

- Stay as is.
- Could there be any other (and better!) total functions that serve the similar purpose?

# Unresolved questions

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