#![no_std]
#![warn(clippy::pedantic)]
#![deny(missing_docs)]
#![allow(clippy::inline_always)]
#[cfg(feature = "unsafe-unchecked")]
use core::hint::unreachable_unchecked;
mod private {
pub trait ToIndex: TryInto<isize> + TryInto<usize> + core::fmt::Debug + Copy {}
impl<T: TryInto<isize> + TryInto<usize> + core::fmt::Debug + Copy> ToIndex for T {}
}
use private::ToIndex;
#[inline(always)]
fn check_index(idx: impl ToIndex, len: usize) -> Option<usize> {
let resolved = if let Ok(unsigned_index) = idx.try_into() {
unsigned_index
} else {
let signed_index = idx.try_into().ok()?;
len.wrapping_add_signed(signed_index)
};
(resolved < len).then_some(resolved)
}
#[cfg(not(feature = "unsafe-unchecked"))]
#[inline(never)]
fn panic_bounds_check(idx: impl ToIndex, len: usize) -> ! {
panic!("index out of bounds: the len is {len} but the index is {idx:?}")
}
pub trait At {
#[inline(always)]
fn at<T>(&self, idx: impl ToIndex) -> T
where
Self: AsRef<[T]>,
T: Copy,
{
let slice = self.as_ref();
let len = slice.len();
match check_index(idx, len) {
Some(i) => slice[i],
#[cfg(feature = "unsafe-unchecked")]
None => unsafe { unreachable_unchecked() },
#[cfg(not(feature = "unsafe-unchecked"))]
None => panic_bounds_check(idx, len),
}
}
#[inline(always)]
fn ref_at<T>(&self, idx: impl ToIndex) -> &T
where
Self: AsRef<[T]>,
{
let slice = self.as_ref();
let len = slice.len();
match check_index(idx, len) {
Some(i) => &slice[i],
#[cfg(feature = "unsafe-unchecked")]
None => unsafe { unreachable_unchecked() },
#[cfg(not(feature = "unsafe-unchecked"))]
None => panic_bounds_check(idx, len),
}
}
#[inline(always)]
fn mut_at<T>(&mut self, idx: impl ToIndex) -> &mut T
where
Self: AsMut<[T]>,
{
let slice = self.as_mut();
let len = slice.len();
match check_index(idx, len) {
Some(i) => &mut slice[i],
#[cfg(feature = "unsafe-unchecked")]
None => unsafe { unreachable_unchecked() },
#[cfg(not(feature = "unsafe-unchecked"))]
None => panic_bounds_check(idx, len),
}
}
}
impl<T> At for T {}
mod test {
#[cfg(test)]
use crate::At;
#[test]
fn test_positive() {
extern crate std;
use std::vec;
let mut v = vec![1, 2, 3];
assert_eq!(v.at(0u8), 1);
assert_eq!(v.ref_at(1i128), &2);
assert_eq!(v.mut_at(2isize), &mut 3);
}
#[test]
fn test_negative() {
let mut v = [4, 5, 6];
assert_eq!(v.at(-1i8), 6);
assert_eq!(v.ref_at(-2i128), &5);
assert_eq!(v.mut_at(-3isize), &mut 4);
}
#[test]
#[should_panic(expected = "index out of bounds: the len is 1 but the index is -2")]
fn test_panic() {
let s = ["hi"];
let _ = s.at(-2);
}
#[test]
fn test_zst() {
let giant = [(); usize::MAX];
giant.at(-1);
giant.at(usize::MAX - 1);
giant.at(isize::MIN);
}
}