1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
//! Adds more index types, improving correctness and clarifying intent.
//!
//! The crate is separated by modules which each roughly explore one particular index-related idea.
//! As an overview, the main modules are:
//!
//! - In the [`mod@array`] module, a solution for repetitive try-into-unwrapping in the process of
//! interpreting slices as fixed-width arrays is presented.
//! - In the [`int`] module, we explore numerical types with fallible conversion to indices on use,
//! interpreting a failure as out-of-bounds.
//! - In the [`mem`] module, the efficient representation for the type intersection between
//! integers and pointer sized indices is presented as a set of concrete types.
//! - In the [`tag`] module, we apply fancy type-level mechanisms to move the bounds check inherent
//! in safe slice accesses from the usage site to the construction of the index. See in
//! particular the Huffman example that demonstrates the optimization potential.
//!
//! Read the [`readme`] for some examples. In short:
//!
//! ```
//! use index_ext::{Intex, SliceIntExt};
//!
//! let fine = [0u8; 2][Intex(1u32)];
//! let also = [0u8; 2][Intex(1u128)];
//!
//! assert_eq!([0u8; 2].get_int(u128::max_value()), None);
//! ```
//!
//! ## Unfinished features
//!
//! The marker WIP means it is worked on, Planned that it might be worked on due to intrigue of the
//! author, and Idea itself is still unevaluated (looking for a usecase, for instance).
//!
//! `Planned`: An index type `CharAt(n: usize)` that dereferences to the characters of a string
//! _around_ a particular position, represented by a string wrapper that allows converting into a
//! `char`. In contrast to the standard operator, this would only panic for out-of-bounds
//! coordinates and thus match the main expectation with regards to `len`.
//!
//! `Idea`: Note that a generic `PrefixChars<N: usize>`, retrieving the first N characters, would
//! not be constant time which may be surprising if used in index position.
//!
//! `Idea`: An index type `InsertWith` for `HashMap` and `BTreeMap` that will construct an
//! element when an entry is missing, similar to C++, and thus be a panic free alternative. _Maybe_
//! we could index a `Vec<_>` with this type as well, extending as necessary, but this would again
//! not be constant time. The problem here is the super trait relationship `IndexMut: Index` which
//! might lead to many potential misuses. Also the common alternative of `entry.or_insert` is both
//! simple an robust already.
//!
//! `Idea`: An adapter `OrEmpty` that uses `get` internally and substitutes an empty slice instead
//! of panicking. Now this is maybe both clear and cute, I'd actually see some use here. It's
//! unclear for which types to provide it and if there's a SemVer risk.
//!
//! ## Design notes
//!
//! The extension traits offered here have a slight ergonomic problem compared to being included in
//! the standard library. Its `ops::Index` impls on slices are provided by the `SliceIndex` trait.
//! Since this is a nightly trait _and_ sealed by design we can not use it. However, we also can
//! not use a generic impl for all `T: crate::SliceIndex<[U]>` as this is forbidden by coherence
//! rules for foreign types. We thus utilize two kinds of indexing: Implement the Index trait
//! directly for all concrete applicable types and provide a single newtype which acts as a proxy
//! for the otherwise unconstrained type parameter of the generic impl. If the types were added to
//! `core` then this indirection would not be necessary and ergonomics would improve.
#![no_std]
#![deny(clippy::missing_safety_doc)]
#![deny(missing_docs)]
#![deny(unsafe_op_in_unsafe_fn)]
#[cfg(feature = "alloc")]
extern crate alloc;
pub mod array;
pub mod int;
pub mod mem;
pub mod tag;
pub use array::ArrayPrefix;
pub use int::SliceIntExt;
/// Convert an arbitrary integer into an index.
///
/// This method simply constructs an inner transparent wrapper struct `Intex` but can be used as an
/// alternative which is imported with the same name, and at the same time, as the trait.
#[allow(non_snake_case)]
pub fn Intex<T>(idx: T) -> int::Intex<T> {
int::Intex(idx)
}
#[cfg(doc)]
macro_rules! doctest_readme {
{ $content:expr } => {
/// A rendered version of the Readme file, documentation purpose only.
///
#[doc = $content] pub mod readme {}
}
}
#[cfg(doc)]
doctest_readme!(include_str!("../Readme.md"));
#[cfg(test)]
mod test {
use super::{Intex, SliceIntExt};
#[test]
#[should_panic = "100"]
fn panics_with_length_u32() {
[0u8; 0][Intex(100u32)];
}
#[test]
#[should_panic = "100"]
fn panics_with_length_u8() {
[0u8; 0][Intex(100u8)];
}
#[test]
#[should_panic = "-1"]
fn panics_with_length_i8() {
[0u8; 0][Intex(-1i8)];
}
#[test]
#[should_panic = "100000000000000000000000000000000000000"]
fn panics_with_length_u128() {
[0u8; 0][Intex(100_000_000_000_000_000_000_000_000_000_000_000_000u128)];
}
#[test]
fn index_with_all() {
let slice = [0u8; 10];
macro_rules! assert_slice_success {
(@$slice:path, $exp:expr) => {
assert!($slice.get_int($exp).is_some());
};
($slice:path: $($exp:expr),*) => {
$(assert_slice_success!(@$slice, $exp);)*
}
}
macro_rules! assert_slice_fail {
(@$slice:path, $exp:expr) => {
assert_eq!($slice.get_int($exp), None);
};
($slice:path: $($exp:expr),*) => {
$(assert_slice_fail!(@$slice, $exp);)*
}
}
assert_slice_success!(slice: 0u8, 0i8, 0u16, 0i16, 0u32, 0i32, 0u64, 0i64);
assert_slice_success!(slice: ..10u8, ..10i8, ..10u16, ..10i16, ..10u32, ..10i32, ..10u64, ..10i64);
assert_slice_success!(slice: 0..10u8, 0..10i8, 0..10u16, 0..10i16, 0..10u32, 0..10i32, 0..10u64, 0..10i64);
assert_slice_success!(slice: 10u8.., 10i8.., 10u16.., 10i16.., 10u32.., 10i32.., 10u64.., 10i64..);
assert_slice_fail!(slice: -1i8, -1i16, -1i32, -1i64);
}
#[test]
fn unchecked() {
let mut slice = [0u8, 1, 2, 3];
macro_rules! assert_slice_eq {
(@$slice:path, $idx:expr, $exp:expr) => {
assert_eq!($slice[Intex($idx)], $exp);
assert_eq!(&mut $slice[Intex($idx)], $exp);
unsafe {
assert_eq!($slice.get_int_unchecked($idx), $exp);
assert_eq!($slice.get_int_unchecked_mut($idx), $exp);
}
};
($slice:path[idx], $result:expr, for idx in [$($idx:expr),*]) => {
$(assert_slice_eq!(@$slice, $idx, $result);)*
}
}
assert_slice_eq!(slice[idx], [1, 2],
for idx in [1u8..3, 1i8..3, 1u16..3, 1i16..3, 1u32..3, 1i32..3, 1u64..3, 1i64..3]);
}
#[test]
fn str_indices() {
let text = "What if ascii still has it?";
assert_eq!(text.get_int(8u8..13), Some("ascii"));
}
}