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 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
//! Adds more index types. //! //! There are cases where an index type might not be `usize`, many of them for compatibility //! reasons. For example, an archive format may choose to always represent its offsets as `u32` or //! the `io::Seek` trait which uses `i64` for that purpose. Translating these indices into the //! platform native offset type is error prone, potentially lossy, and in case it is done //! incorrectly leads to subtle platform dependent bugs. //! //! Wouldn't it be better for this conversion to happen implicitly and correctly where the actual //! indexing takes place? That's precisely what `Index` provides. (It's a method and a trait of //! the same name, for both panicking and fallible accessors). //! //! ``` //! use index_ext::Int; //! //! let fine = [0u8; 2][Int(1u32)]; //! let also = [0u8; 2][Int(1u128)]; //! //! assert_eq!([0u8; 2].get_int(u128::max_value()), None); //! ``` //! //! ## Nightly features //! //! * The `RangeTo` type is a const generics enabled index that return arrays `[T; N]` instead of //! slices. Due to recent advances in parameter deduction, the length parameter need not even be //! named. //! #![cfg_attr(feature = "nightly", doc = "```")] #![cfg_attr(not(feature = "nightly"), doc = "```ignore")] //! # let slice = [0; 4]; //! use index_ext::array::RangeTo; //! // Grab an array of three element from a slice. //! let [r, g, b] = &slice[RangeTo]; //! ``` //! //! ## Unfinished features //! //! The marker WIP means it is worked on, Planned that it will be worked on, and Idea that it is //! still unevaluated but might be interesting. //! //! [Planned]: An index type `CharAt(n: usize)` that dereferences to the characters of a string at //! a particular position, represented by a string wrapper that allows converting into a `char`. //! Note that a generic `Chars` 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. //! //! [Idea]: An adapter `OrEmpty` that uses `get` internally and substitutes an empty slice instead //! of panicking. //! //! ## 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] #![cfg_attr(feature = "nightly", feature(const_generics))] mod sealed { /// Seals the `Int` extension trait. /// The methods added there are intended to be like inherent methods on the respective /// implementors which means additional implementors are not intended. pub trait Sealed {} } #[cfg(feature = "nightly")] pub mod array; pub mod int; /// A trait for integer based indices. /// /// Any integer can be used as a fallible index where a machine word can be used by first trying to /// convert it into a `usize` and then indexing with the original method. From the point of the /// user, the effect is not much different. If `10usize` is out-of-bounds then so is any other /// integer representing the number `10`, no matter the allowed magnitude of its type. The same /// holds for integers that permit negative indices. /// /// The output type of the indexing operation is an element or a slice respectively. /// /// This trait enables the generic [`Int::get_int`] method. /// /// [`Int::get_int`]: trait.Int.html#fn.get_int pub trait IntSliceIndex<T: ?Sized>: int::sealed::SealedSliceIndex<T> {} /// An extension trait allowing slices to be indexed by everything convertible to `usize`. pub trait Int: sealed::Sealed { /// Return a reference to an element or subslice with an integer index, or `None` if out of /// bounds. /// /// This works like [`slice::get`] but allows arbitrary integers to be used as indices. It will /// first try to convert them to an `usize`. For some types (`u8` and `u16`) this can never /// fail while other types may refer to negative indices or are out-of-range. These cases are /// treated as if the index was out-of-bounds due to the slice being too short. /// /// ## Examples /// /// ``` /// # use index_ext::Int; /// let v = [10, 40, 30]; /// assert_eq!(Some(&40), v.get_int(1u64)); /// assert_eq!(Some(&[10, 40][..]), v.get_int(0u8..2)); /// assert_eq!(None, v.get_int(3u8)); /// assert_eq!(None, v.get_int(0u8..4)); /// assert_eq!(None, v.get_int(-1i8)); /// ``` /// /// [`slice::get`]: https://doc.rust-lang.org/stable/std/primitive.slice.html#method.get fn get_int<T>(&self, idx: T) -> Option<&'_ <T as int::sealed::IntSliceIndex<Self>>::Output> where T: IntSliceIndex<Self>; /// Return a mutable reference to an element or subslice with an integer index, or `None` if /// out of bounds. /// /// This works like [`slice::get_mut`]. /// /// ## Examples /// /// ``` /// # use index_ext::Int; /// let x = &mut [0, 1, 2]; /// /// if let Some(elem) = x.get_int_mut(1u8) { /// *elem = 42; /// } /// assert_eq!(x, &[0, 42, 2]); /// ``` /// /// [`slice::get_mut`]: https://doc.rust-lang.org/stable/std/primitive.slice.html#method.get_mut fn get_int_mut<T>( &mut self, idx: T, ) -> Option<&'_ mut <T as int::sealed::IntSliceIndex<Self>>::Output> where T: IntSliceIndex<Self>; /// Returns a reference to an element or subslice without doing bounds checking. /// /// ## Safety /// /// Like [`slice::get_unchecked`], calling this method with an out of bounds index is undefined /// behaviour. _This includes indices for which conversion to a `usize` fails._ /// /// [`slice::get_unchecked`]: https://doc.rust-lang.org/stable/std/primitive.slice.html#method.get_unchecked /// /// ## Examples /// ``` /// # use index_ext::Int; /// let x = &[1, 2, 4]; /// /// unsafe { /// assert_eq!(x.get_int_unchecked(1i8), &2); /// } /// ``` unsafe fn get_int_unchecked<T>( &self, idx: T, ) -> &'_ <T as int::sealed::IntSliceIndex<Self>>::Output where T: IntSliceIndex<Self>; /// Returns a mutable reference to an element or subslice without doing bounds checking. /// /// ## Safety /// /// Like [`slice::get_unchecked_mut`], calling this method with an out of bounds index is undefined /// behaviour. _This includes indices for which conversion to a `usize` fails._ /// /// ## Examples /// /// ``` /// # use index_ext::Int; /// let x = &mut [1, 2, 4]; /// /// unsafe { /// let elem = x.get_int_unchecked_mut(1u64); /// *elem = 13; /// } /// /// assert_eq!(x, &[1, 13, 4]); /// ``` /// /// [`slice::get_unchecked_mut`]: https://doc.rust-lang.org/stable/std/primitive.slice.html#method.get_unchecked_mut unsafe fn get_int_unchecked_mut<T>( &mut self, idx: T, ) -> &'_ mut <T as int::sealed::IntSliceIndex<Self>>::Output where T: IntSliceIndex<Self>; } impl<U> sealed::Sealed for [U] {} impl<U> Int for [U] { fn get_int<T>(&self, idx: T) -> Option<&'_ <T as int::sealed::IntSliceIndex<Self>>::Output> where T: IntSliceIndex<Self>, { <T as int::sealed::IntSliceIndex<Self>>::get(idx, self) } fn get_int_mut<T>( &mut self, idx: T, ) -> Option<&'_ mut <T as int::sealed::IntSliceIndex<Self>>::Output> where T: IntSliceIndex<Self>, { <T as int::sealed::IntSliceIndex<Self>>::get_mut(idx, self) } unsafe fn get_int_unchecked<T>( &self, idx: T, ) -> &'_ <T as int::sealed::IntSliceIndex<Self>>::Output where T: IntSliceIndex<Self>, { <T as int::sealed::IntSliceIndex<Self>>::get_unchecked(idx, self) } unsafe fn get_int_unchecked_mut<T>( &mut self, idx: T, ) -> &'_ mut <T as int::sealed::IntSliceIndex<Self>>::Output where T: IntSliceIndex<Self>, { <T as int::sealed::IntSliceIndex<Self>>::get_unchecked_mut(idx, self) } } impl sealed::Sealed for str {} impl Int for str { fn get_int<T>(&self, idx: T) -> Option<&'_ <T as int::sealed::IntSliceIndex<Self>>::Output> where T: IntSliceIndex<Self>, { <T as int::sealed::IntSliceIndex<Self>>::get(idx, self) } fn get_int_mut<T>( &mut self, idx: T, ) -> Option<&'_ mut <T as int::sealed::IntSliceIndex<Self>>::Output> where T: IntSliceIndex<Self>, { <T as int::sealed::IntSliceIndex<Self>>::get_mut(idx, self) } unsafe fn get_int_unchecked<T>( &self, idx: T, ) -> &'_ <T as int::sealed::IntSliceIndex<Self>>::Output where T: IntSliceIndex<Self>, { <T as int::sealed::IntSliceIndex<Self>>::get_unchecked(idx, self) } unsafe fn get_int_unchecked_mut<T>( &mut self, idx: T, ) -> &'_ mut <T as int::sealed::IntSliceIndex<Self>>::Output where T: IntSliceIndex<Self>, { <T as int::sealed::IntSliceIndex<Self>>::get_unchecked_mut(idx, self) } } /// Convert an arbitrary integer into an index. /// /// This method simply constructs an inner transparent wrapper struct `Int` 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 Int<T>(idx: T) -> int::Int<T> { int::Int(idx) } #[cfg(test)] mod test { use super::Int; #[test] #[should_panic = "100"] fn panics_with_length_u32() { [0u8; 0][Int(100u32)]; } #[test] #[should_panic = "100"] fn panics_with_length_u8() { [0u8; 0][Int(100u8)]; } #[test] #[should_panic = "-1"] fn panics_with_length_i8() { [0u8; 0][Int(-1i8)]; } #[test] #[should_panic = "100000000000000000000000000000000000000"] fn panics_with_length_u128() { [0u8; 0][Int(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[Int($idx)], $exp); assert_eq!(&mut $slice[Int($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")); } }