Skip to main content

try_index/
lib.rs

1//! A boilerplate library that provides traits [TryIndex] and [TryIndexMut].
2//! Unlike the ones in [try_traits](https://docs.rs/try-traits/latest/try_traits/),
3//! this gives explicit implementations for all standard library collections,
4//! which can actually fail. In exchange, no blanket implementation is provided.
5//!
6//! The idea is to provide a single definition of these traits
7//! that other crates may use to interface with each other in a generic way,
8//! while carrying no further code with it.
9//!
10//! This crate has a default `std` feature that may be disabled for `no_std` support,
11//! as well as an `alloc` feature that enables `no_std` `alloc` container types.
12//!
13//! ## Examples
14//! ### Basic usage
15//! The methods in this crate return `None` on failure rather than panicking.
16//!
17//! ```rust
18//! use try_index::*;
19//!
20//! let foo = vec![4, 3, 6, 2];
21//! assert_eq!(foo.try_index(2), Some(&6));
22//! assert_eq!(foo.try_index(4), None);
23//! assert_eq!(foo.try_index(1..=2), Some(&[3, 6][..]));
24//! ```
25//!
26//! ### Implementing on custom type
27//! With this, you can provide fallible indexing for your own types.
28//!
29//! ```rust
30//! use try_index::*;
31//!
32//! struct YourType {
33//! }
34//!
35//! impl TryIndex<usize> for YourType {
36//!     type Output = u8;
37//!     
38//!     fn try_index(&self, index: usize) -> Option<&Self::Output> {
39//!         todo!() // your implementation
40//!     }
41//! }
42//!
43//! let foo = YourType {};
44//! // now, simply call foo.try_index()
45//! ```
46//!
47//! ### Implementing on third-party type
48//! This allows you to glue together libraries not providing [TryIndex]
49//! and others requiring it, within your code.
50//!
51//! ```rust
52//! use try_index::*;
53//!
54//! // defined elsewhere
55//! pub struct NotYourType {
56//! }
57//!
58//! // can't implement directly, so we use a wrapper
59//! #[repr(transparent)]
60//! struct YourWrapper<'a> {
61//!     inner: &'a NotYourType,
62//! }
63//!
64//! impl<'a> TryIndex<usize> for YourWrapper<'a> {
65//!     type Output = u8;
66//!     
67//!     fn try_index(&self, index: usize) -> Option<&Self::Output> {
68//!         todo!() // your implementation
69//!     }
70//! }
71//!
72//! let foo = NotYourType {};
73//! let wrapper = YourWrapper {inner: &foo};
74//! // now, call wrapper.try_index() or pass wrapper around
75//! ```
76
77#![cfg_attr(not(feature = "std"), no_std)]
78#[cfg(not(feature = "std"))]
79use core::{ffi::CStr, ops::RangeFrom, slice, slice::SliceIndex};
80#[cfg(feature = "std")]
81use std::{
82    borrow::Borrow,
83    collections::{BTreeMap, HashMap, VecDeque},
84    ffi::{CStr, CString, OsStr, OsString},
85    hash::Hash,
86    ops::{RangeFrom, RangeFull},
87    slice,
88    slice::SliceIndex,
89};
90#[cfg(all(not(feature = "std"), feature = "alloc"))]
91extern crate alloc;
92#[cfg(all(not(feature = "std"), feature = "alloc"))]
93use alloc::{
94    borrow::Borrow,
95    collections::{btree_map::BTreeMap, vec_deque::VecDeque},
96    ffi::CString,
97    string::String,
98    vec::Vec,
99};
100#[cfg(all(not(feature = "std"), feature = "alloc"))]
101use core::ops::RangeFull;
102
103/*
104 * Traits
105 */
106/// A fallible version of [Index](https://doc.rust-lang.org/std/ops/trait.Index.html)
107/// that will return `None` if the indexing operation fails.
108pub trait TryIndex<Idx>
109where
110    Idx: ?Sized,
111{
112    type Output: ?Sized;
113
114    fn try_index(&self, index: Idx) -> Option<&Self::Output>;
115}
116
117/// A fallible version of [IndexMut](https://doc.rust-lang.org/std/ops/trait.IndexMut.html)
118/// that will return `None` if the indexing operation fails.
119pub trait TryIndexMut<Idx>
120where
121    Idx: ?Sized,
122{
123    type Output: ?Sized;
124
125    fn try_index_mut(&mut self, index: Idx) -> Option<&mut Self::Output>;
126}
127
128/*
129 * Slices
130 */
131impl<T, I> TryIndex<I> for [T]
132where
133    I: SliceIndex<[T]>,
134{
135    type Output = <I as SliceIndex<[T]>>::Output;
136
137    fn try_index(&self, index: I) -> Option<&Self::Output> {
138        self.get(index)
139    }
140}
141
142impl<T, I> TryIndexMut<I> for [T]
143where
144    I: SliceIndex<[T]>,
145{
146    type Output = <I as SliceIndex<[T]>>::Output;
147
148    fn try_index_mut(&mut self, index: I) -> Option<&mut Self::Output> {
149        self.get_mut(index)
150    }
151}
152
153/*
154 * Arrays
155 */
156impl<T, I, const N: usize> TryIndex<I> for [T; N]
157where
158    [T]: TryIndex<I, Output = T>,
159{
160    type Output = T;
161
162    fn try_index(&self, index: I) -> Option<&Self::Output> {
163        self.as_slice().try_index(index)
164    }
165}
166
167impl<T, I, const N: usize> TryIndexMut<I> for [T; N]
168where
169    [T]: TryIndexMut<I, Output = T>,
170{
171    type Output = T;
172
173    fn try_index_mut(&mut self, index: I) -> Option<&mut Self::Output> {
174        self.as_mut_slice().try_index_mut(index)
175    }
176}
177
178/*
179 * Vec
180 */
181#[cfg(any(feature = "std", feature = "alloc"))]
182impl<T, I> TryIndex<I> for Vec<T>
183where
184    I: SliceIndex<[T]>,
185{
186    type Output = <I as SliceIndex<[T]>>::Output;
187
188    fn try_index(&self, index: I) -> Option<&Self::Output> {
189        self.get(index)
190    }
191}
192
193#[cfg(any(feature = "std", feature = "alloc"))]
194impl<T, I> TryIndexMut<I> for Vec<T>
195where
196    I: SliceIndex<[T]>,
197{
198    type Output = <I as SliceIndex<[T]>>::Output;
199
200    fn try_index_mut(&mut self, index: I) -> Option<&mut Self::Output> {
201        self.get_mut(index)
202    }
203}
204
205/*
206 * VecDeque
207 */
208#[cfg(any(feature = "std", feature = "alloc"))]
209impl<T> TryIndex<usize> for VecDeque<T> {
210    type Output = T;
211
212    fn try_index(&self, index: usize) -> Option<&Self::Output> {
213        self.get(index)
214    }
215}
216
217#[cfg(any(feature = "std", feature = "alloc"))]
218impl<T> TryIndexMut<usize> for VecDeque<T> {
219    type Output = T;
220
221    fn try_index_mut(&mut self, index: usize) -> Option<&mut Self::Output> {
222        self.get_mut(index)
223    }
224}
225
226/*
227 * HashMap
228 */
229#[cfg(feature = "std")]
230impl<K, Q, V> TryIndex<&Q> for HashMap<K, V>
231where
232    K: Eq + Hash + Borrow<Q>,
233    Q: Eq + Hash + ?Sized,
234{
235    type Output = V;
236
237    fn try_index(&self, index: &Q) -> Option<&Self::Output> {
238        self.get(index)
239    }
240}
241
242#[cfg(feature = "std")]
243impl<K, Q, V> TryIndexMut<&Q> for HashMap<K, V>
244where
245    K: Eq + Hash + Borrow<Q>,
246    Q: Eq + Hash + ?Sized,
247{
248    type Output = V;
249
250    fn try_index_mut(&mut self, index: &Q) -> Option<&mut Self::Output> {
251        self.get_mut(index)
252    }
253}
254
255/*
256 * BTreeMap
257 */
258#[cfg(any(feature = "std", feature = "alloc"))]
259impl<K, Q, V> TryIndex<&Q> for BTreeMap<K, V>
260where
261    K: Borrow<Q> + Ord,
262    Q: Ord + ?Sized,
263{
264    type Output = V;
265
266    fn try_index(&self, index: &Q) -> Option<&Self::Output> {
267        self.get(index)
268    }
269}
270
271#[cfg(any(feature = "std", feature = "alloc"))]
272impl<K, Q, V> TryIndexMut<&Q> for BTreeMap<K, V>
273where
274    K: Borrow<Q> + Ord,
275    Q: Ord + ?Sized,
276{
277    type Output = V;
278
279    fn try_index_mut(&mut self, index: &Q) -> Option<&mut Self::Output> {
280        self.get_mut(index)
281    }
282}
283
284/*
285 * str
286 */
287#[cfg(any(feature = "std", feature = "alloc"))]
288impl<I> TryIndex<I> for str
289where
290    I: SliceIndex<str>,
291{
292    type Output = <I as SliceIndex<str>>::Output;
293
294    fn try_index(&self, index: I) -> Option<&Self::Output> {
295        self.get(index)
296    }
297}
298
299#[cfg(any(feature = "std", feature = "alloc"))]
300impl<I> TryIndexMut<I> for str
301where
302    I: SliceIndex<str>,
303{
304    type Output = <I as SliceIndex<str>>::Output;
305
306    fn try_index_mut(&mut self, index: I) -> Option<&mut Self::Output> {
307        self.get_mut(index)
308    }
309}
310
311/*
312 * String
313 */
314#[cfg(any(feature = "std", feature = "alloc"))]
315impl<I> TryIndex<I> for String
316where
317    I: SliceIndex<str>,
318{
319    type Output = <I as SliceIndex<str>>::Output;
320
321    fn try_index(&self, index: I) -> Option<&Self::Output> {
322        self.get(index)
323    }
324}
325
326#[cfg(any(feature = "std", feature = "alloc"))]
327impl<I> TryIndexMut<I> for String
328where
329    I: SliceIndex<str>,
330{
331    type Output = <I as SliceIndex<str>>::Output;
332
333    fn try_index_mut(&mut self, index: I) -> Option<&mut Self::Output> {
334        self.get_mut(index)
335    }
336}
337
338/*
339 * CStr
340 */
341impl TryIndex<RangeFrom<usize>> for CStr {
342    type Output = CStr;
343
344    fn try_index(&self, index: RangeFrom<usize>) -> Option<&Self::Output> {
345        let l = self.count_bytes();
346        match index.start <= l {
347            true => Some(unsafe {
348                CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(
349                    &*(self.as_ptr() as *const u8).add(index.start),
350                    l - index.start + 1,
351                ))
352            }),
353            false => None,
354        }
355    }
356}
357
358#[test]
359fn cstr_test() {
360    let string = c"Test";
361    assert_eq!(string.try_index(2..), Some(c"st"));
362    assert_eq!(string.try_index(4..), Some(c""));
363    assert_eq!(string.try_index(5..), None);
364}
365
366/*
367 * CString
368 */
369#[cfg(any(feature = "std", feature = "alloc"))]
370impl TryIndex<RangeFull> for CString {
371    type Output = CStr;
372
373    fn try_index(&self, index: RangeFull) -> Option<&Self::Output> {
374        Some(&self[index])
375    }
376}
377
378/*
379 * OsString
380 */
381#[cfg(feature = "std")]
382impl TryIndex<RangeFull> for OsString {
383    type Output = OsStr;
384
385    fn try_index(&self, index: RangeFull) -> Option<&Self::Output> {
386        Some(&self[index])
387    }
388}