This is already possible in Rust for x[LEN - 1]
, x[(LEN - 1)..]
and x[..(LEN - 1)]
, by giving LEN
a new type. This is also how C#'s index expression works at type level.
The only problem is we can’t support x[2..(LEN-1)]
because Range<T>
expects both sides of the ..
to have the same type, you’ll need to write x[2..][..(LEN-1)]
.
C# solves this problem by allowing int
to be implicitly coerced to Index
, which we will probably never allow. We could desugar x..y
to Range { start: x.into(), end: y.into() }
but that will likely cause lots of type inference problem.
Demo
#![feature(slice_get_slice)]
use std::ops::*;
use std::slice::SliceIndex;
#[derive(Copy, Clone)]
pub struct LengthRelative {
delta: usize,
}
const LEN: LengthRelative = LengthRelative { delta: 0 };
// Prevents error E0210 in this demo code.
struct SkipOrphanRule<S>(S);
fn main() {
let sl = [1, 3, 5, 7, 9, 11];
assert_eq!(9, sl[LEN-2]);
assert_eq!(&[1,3,5,7,9], &sl[SkipOrphanRule(..LEN-1)]);
assert_eq!(&[7,9,11], &sl[SkipOrphanRule(LEN-3..)]);
}
impl Add<usize> for LengthRelative {
type Output = LengthRelative;
fn add(self, delta: usize) -> LengthRelative {
LengthRelative { delta: self.delta - delta }
}
}
impl Sub<usize> for LengthRelative {
type Output = LengthRelative;
fn sub(self, delta: usize) -> LengthRelative {
LengthRelative { delta: self.delta + delta }
}
}
impl<T> SliceIndex<[T]> for LengthRelative {
type Output = T;
fn get(self, slice: &[T]) -> Option<&T> {
let i = slice.len().checked_sub(self.delta)?;
slice.get(i)
}
fn get_mut(self, slice: &mut [T]) -> Option<&mut T> {
let i = slice.len().checked_sub(self.delta)?;
slice.get_mut(i)
}
unsafe fn get_unchecked(self, slice: &[T]) -> &T {
let i = slice.len().wrapping_sub(self.delta);
slice.get_unchecked(i)
}
unsafe fn get_unchecked_mut(self, slice: &mut [T]) -> &mut T {
let i = slice.len().wrapping_sub(self.delta);
slice.get_unchecked_mut(i)
}
fn index(self, slice: &[T]) -> &T {
let i = slice.len() - self.delta;
&slice[i]
}
fn index_mut(self, slice: &mut [T]) -> &mut T {
let i = slice.len() - self.delta;
&mut slice[i]
}
}
impl<T> SliceIndex<[T]> for SkipOrphanRule<RangeFrom<LengthRelative>> {
type Output = [T];
fn get(self, slice: &[T]) -> Option<&[T]> {
let i = slice.len().checked_sub(self.0.start.delta)?;
slice.get(i..)
}
fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> {
let i = slice.len().checked_sub(self.0.start.delta)?;
slice.get_mut(i..)
}
unsafe fn get_unchecked(self, slice: &[T]) -> &[T] {
let i = slice.len().wrapping_sub(self.0.start.delta);
slice.get_unchecked(i..)
}
unsafe fn get_unchecked_mut(self, slice: &mut [T]) -> &mut [T] {
let i = slice.len().wrapping_sub(self.0.start.delta);
slice.get_unchecked_mut(i..)
}
fn index(self, slice: &[T]) -> &[T] {
let i = slice.len() - self.0.start.delta;
&slice[i..]
}
fn index_mut(self, slice: &mut [T]) -> &mut [T] {
let i = slice.len() - self.0.start.delta;
&mut slice[i..]
}
}
impl<T> SliceIndex<[T]> for SkipOrphanRule<RangeTo<LengthRelative>> {
type Output = [T];
fn get(self, slice: &[T]) -> Option<&[T]> {
let i = slice.len().checked_sub(self.0.end.delta)?;
slice.get(..i)
}
fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> {
let i = slice.len().checked_sub(self.0.end.delta)?;
slice.get_mut(..i)
}
unsafe fn get_unchecked(self, slice: &[T]) -> &[T] {
let i = slice.len().wrapping_sub(self.0.end.delta);
slice.get_unchecked(..i)
}
unsafe fn get_unchecked_mut(self, slice: &mut [T]) -> &mut [T] {
let i = slice.len().wrapping_sub(self.0.end.delta);
slice.get_unchecked_mut(..i)
}
fn index(self, slice: &[T]) -> &[T] {
let i = slice.len() - self.0.end.delta;
&slice[..i]
}
fn index_mut(self, slice: &mut [T]) -> &mut [T] {
let i = slice.len() - self.0.end.delta;
&mut slice[..i]
}
}
One issue we can’t directly apply D’s opDollar to Rust is that we also have the get_unchecked
and get_mut
methods. In D, x[y..z]
would invoke x.opSlice(y, z)
, and you can’t use y..z
outside of indexing.
x[5, 6..7, $-8]
== x.opIndex(5, x.opSlice!1(6, 7), x.opDollar!2 - 8)
you can’t have the equivalent of this in D using ..
:
auto r = 0 .. $-1;
// how do I know we should call `x.opDollar!0` here?
x.get_mut(r)