array_section/
lib.rs

1//! An [`ArraySection`] is an array where only a (contiguous) subsection of its data may be viewed.
2//!
3//! This can be useful in const functions that wish to return an array of size `N`,
4//! but with some elements potentially unused.
5//!
6//! The crate is `no_std` compatible.
7//!
8//! ```
9//! # use array_section::ArraySection;
10//! /// Returns an array of the square numbers smaller than both x and N.
11//! const fn squares_smaller_than<const N: usize>(x: usize) -> ArraySection<usize, N> {
12//!     let mut i = 0;
13//!     let mut ans = [0; N];
14//!     while i * i < N && i * i < x {
15//!         ans[i] = i * i;
16//!         i += 1;
17//!     }
18//!     ArraySection::new(ans, 0..i)
19//! }
20//! assert_eq!(squares_smaller_than::<10>(16), [0, 1, 4, 9]);
21//! ```
22//!
23//! # Feature flags
24//!
25//! `std`: lets the error type provide a [`Backtrace`].
26//! When this feature is disabled the crate is `no_std` compatible.
27//! Enables the `alloc` feature.
28//!
29//! `alloc`: enables conversion of the section into [`Vec`]s and [`Box`]ed slices.
30
31#![cfg_attr(not(feature = "std"), no_std)]
32#![cfg_attr(docsrs, feature(doc_auto_cfg))]
33
34use core::{
35    cmp::Ordering,
36    hash::{Hash, Hasher},
37    iter::FusedIterator,
38    ops::{Index, Range},
39    slice::SliceIndex,
40};
41
42#[cfg(feature = "std")]
43use std::backtrace::Backtrace;
44
45#[cfg(all(feature = "alloc", not(feature = "std")))]
46extern crate alloc;
47#[cfg(all(feature = "alloc", not(feature = "std")))]
48use alloc::{boxed::Box, vec::Vec};
49
50/// An array where only a section of the data may be viewed,
51/// as the other data may e.g. not uphold some invariant.
52///
53/// Indexing into the `ArraySection` indexes only into the section:
54/// ```
55/// # use array_section::ArraySection;
56/// //                                                     v  v
57/// const AS: ArraySection<i32, 4> = ArraySection::new([0, 1, 2, 0], 1..3);
58/// assert_eq![AS[0], 1];
59/// assert_eq![AS[1], 2];
60/// ```
61///
62/// The other data is not considered in comparisons, ordering or hashing:
63/// ```
64/// # use array_section::ArraySection;
65/// //                       v  v
66/// const A1: [i32; 4] = [1, 3, 7, 1];
67/// const A2: [i32; 5] = [0, 3, 7, 100, -5];
68/// const AS1: ArraySection<i32, 4> = ArraySection::new(A1, 1..3);
69/// const AS2: ArraySection<i32, 5> = ArraySection::new(A2, 1..3);
70///
71/// // Even though the arrays are different
72/// assert_ne!(A1.as_slice(), A2.as_slice());
73/// // The sections are the same
74/// assert_eq!(AS1, AS2);
75/// ```
76#[derive(Debug, Clone, Copy, Eq)]
77pub struct ArraySection<T, const N: usize> {
78    start: usize,
79    end: usize,
80    array: [T; N],
81}
82
83/// Only hashes the data in the section, and not the full array.
84impl<const N: usize, T: Hash> Hash for ArraySection<T, N> {
85    #[inline]
86    fn hash<H: Hasher>(&self, state: &mut H) {
87        self.as_slice().hash(state);
88    }
89}
90
91/// Only checks the data in the sections, and not the full arrays.
92impl<const N: usize, const M: usize, T: PartialOrd> PartialOrd<ArraySection<T, M>>
93    for ArraySection<T, N>
94{
95    #[inline]
96    fn partial_cmp(&self, other: &ArraySection<T, M>) -> Option<Ordering> {
97        self.as_slice().partial_cmp(other.as_slice())
98    }
99}
100
101/// Only compares the data in the sections and not the full arrays.
102impl<const N: usize, T: Ord> Ord for ArraySection<T, N> {
103    #[inline]
104    fn cmp(&self, other: &Self) -> Ordering {
105        self.as_slice().cmp(other.as_slice())
106    }
107}
108
109impl<const N: usize, T> ArraySection<T, N> {
110    /// Restrict an array so that only elements within the given range are visible.
111    ///
112    /// # Panics
113    ///
114    /// Panics if the range of indices is out of bounds of the array.
115    #[inline]
116    pub const fn new(array: [T; N], section: Range<usize>) -> Self {
117        assert!(
118            section.start < N && section.end <= N,
119            "the sub-range must be in bounds"
120        );
121
122        if section.start > section.end {
123            Self {
124                start: 0,
125                end: 0,
126                array,
127            }
128        } else {
129            Self {
130                start: section.start,
131                end: section.end,
132                array,
133            }
134        }
135    }
136
137    /// Returns the first index of the full underlying array that is part of the section.
138    /// I.e. the section is the subrange `start ..`[`end`](ArraySection::end).
139    #[inline]
140    pub const fn start(&self) -> usize {
141        self.start
142    }
143
144    /// Returns the first index of the full underlying array that is outside the section (to the right).
145    /// I.e. the section is the subrange [`start`](ArraySection::start)`.. end`.
146    #[inline]
147    pub const fn end(&self) -> usize {
148        self.end
149    }
150
151    /// Changes the section of the array to the given one.
152    ///
153    /// # Panics
154    ///
155    /// Panics if the start and/or end of the given section is out of bounds of the array.
156    #[inline]
157    pub fn change_section(&mut self, section: Range<usize>) {
158        assert!(
159            section.start < N && section.end <= N,
160            "the section must be in bounds"
161        );
162        if section.start > section.end {
163            self.start = 0;
164            self.end = 0;
165        } else {
166            self.start = section.start;
167            self.end = section.end;
168        }
169    }
170
171    /// Returns a reference to the full underlying array if it is fully populated.
172    #[inline]
173    pub const fn try_as_full_array(&self) -> Option<&[T; N]> {
174        if self.section_is_full_array() {
175            Some(&self.array)
176        } else {
177            None
178        }
179    }
180
181    /// Returns a mutable reference to the full underlying array if it is fully populated.
182    #[inline]
183    pub fn try_as_full_array_mut(&mut self) -> Option<&mut [T; N]> {
184        self.section_is_full_array().then_some(&mut self.array)
185    }
186
187    /// Returns a reference to the full underlying array.
188    #[inline]
189    pub const fn as_full_array(&self) -> &[T; N] {
190        &self.array
191    }
192
193    /// Returns a mutable reference to the full underlying array.
194    #[inline]
195    pub fn as_full_array_mut(&mut self) -> &mut [T; N] {
196        &mut self.array
197    }
198
199    /// Splits the underlying array into three slices: the part before the section,
200    /// the section, and the part after the section.
201    #[inline]
202    pub const fn split_at_section(&self) -> (&[T], &[T], &[T]) {
203        let (head, rest) = self.array.split_at(self.start);
204        let (section, tail) = rest.split_at(self.end - self.start);
205        (head, section, tail)
206    }
207
208    /// Splits the underlying array into three mutable slices: the part before the section,
209    /// the section, and the part after the seciton.
210    #[inline]
211    pub fn split_at_section_mut(&mut self) -> (&mut [T], &mut [T], &mut [T]) {
212        let (head, rest) = self.array.split_at_mut(self.start);
213        let (section, tail) = rest.split_at_mut(self.end - self.start);
214        (head, section, tail)
215    }
216
217    /// Converts `self` into the full underlying array.
218    ///
219    /// If you wish to use this in const context the destructor of `T` must be trivial,
220    /// use [`into_full_array_const`](ArraySection::into_full_array_const)
221    #[inline]
222    pub fn into_full_array(self) -> [T; N] {
223        self.array
224    }
225
226    /// Returns the section of the array as a slice.
227    #[inline]
228    pub const fn as_slice(&self) -> &[T] {
229        self.split_at_section().1
230    }
231
232    /// Returns the section of the array as a mutable slice.
233    #[inline]
234    pub fn as_slice_mut(&mut self) -> &mut [T] {
235        self.split_at_section_mut().1
236    }
237
238    /// Returns the length of the array section.
239    #[inline]
240    pub const fn len(&self) -> usize {
241        self.as_slice().len()
242    }
243
244    /// Returns whether the array section is empty.
245    #[inline]
246    pub const fn is_empty(&self) -> bool {
247        self.as_slice().is_empty()
248    }
249
250    /// Returns whether the section is just the entire array.
251    /// If this is `true` it is completely fine to call [`as_full_array`](ArraySection::as_full_array)
252    /// or [`into_full_array`](ArraySection::into_full_array).
253    #[inline]
254    pub const fn section_is_full_array(&self) -> bool {
255        self.len() == N
256    }
257
258    /// Returns an iterator over the array section.
259    #[inline]
260    pub fn iter(&self) -> ArraySectionIter<'_, T> {
261        ArraySectionIter::new(self.as_slice().iter())
262    }
263
264    /// Returns a mutable iterator over the array section.
265    #[inline]
266    pub fn iter_mut(&mut self) -> ArraySectionIterMut<'_, T> {
267        ArraySectionIterMut::new(self.as_slice_mut().iter_mut())
268    }
269
270    #[cfg(any(feature = "alloc", feature = "std"))]
271    /// Converts the array section into a vector.
272    ///
273    /// # Example
274    ///
275    /// ```
276    /// # use array_section::ArraySection;
277    /// //                                     v  v  v
278    /// let section = ArraySection::new([0, 0, 1, 2, 3, 0, 0], 2..5);
279    ///
280    /// assert_eq!(section.into_vec(), vec![1, 2, 3]);
281    /// ```
282    #[inline]
283    pub fn into_vec(self) -> Vec<T> {
284        self.into_iter().collect()
285    }
286
287    #[cfg(any(feature = "alloc", feature = "std"))]
288    /// Converts the array section into a boxed slice.
289    ///
290    /// # Example
291    ///
292    /// ```
293    /// # use array_section::ArraySection;
294    /// //                                     v  v  v
295    /// let section = ArraySection::new([0, 0, 1, 2, 3, 0, 0], 2..5);
296    ///
297    /// assert_eq!(
298    ///     section.into_boxed_slice(),
299    ///     Box::new([1, 2, 3]) as Box<[i32]>
300    /// );
301    /// ```
302    #[inline]
303    pub fn into_boxed_slice(self) -> Box<[T]> {
304        self.into_vec().into_boxed_slice()
305    }
306}
307
308impl<T: Clone, const N: usize> ArraySection<T, N> {
309    #[cfg(any(feature = "alloc", feature = "std"))]
310    /// Clones the contents of the array section into a vector.
311    #[inline]
312    pub fn to_vec(&self) -> Vec<T> {
313        self.as_slice().to_vec()
314    }
315
316    #[cfg(any(feature = "alloc", feature = "std"))]
317    /// Clones the contents of the array section into a boxed slice.
318    #[inline]
319    pub fn to_boxed_slice(&self) -> Box<[T]> {
320        self.as_slice().into()
321    }
322}
323
324#[cfg(any(feature = "alloc", feature = "std"))]
325impl<T: Clone, const N: usize> From<ArraySection<T, N>> for Vec<T> {
326    /// Clones the contents of the section into a [`Vec`].
327    #[inline]
328    fn from(value: ArraySection<T, N>) -> Vec<T> {
329        value.as_slice().into()
330    }
331}
332#[cfg(any(feature = "alloc", feature = "std"))]
333impl<T: Clone, const N: usize> From<ArraySection<T, N>> for Box<[T]> {
334    /// Clones the contents of the section into a [`Box`]ed slice.
335    #[inline]
336    fn from(value: ArraySection<T, N>) -> Box<[T]> {
337        value.as_slice().into()
338    }
339}
340
341impl<T: Copy, const N: usize> ArraySection<T, N> {
342    /// Converts `self` into the full underlying array.
343    #[inline]
344    pub const fn into_full_array_const(self) -> [T; N] {
345        self.array
346    }
347}
348
349// region: TryFrom impls
350
351/// Returned when a `TryFrom` conversion of an [`ArraySection`] into an array fails.
352///
353/// Contains the original `ArraySection`, which can be retrieved via the [`array_section`](TryFromArraySectionError::array_section) function.
354#[derive(Debug)]
355pub struct TryFromArraySectionError<T, const N: usize> {
356    section: ArraySection<T, N>,
357    #[cfg(feature = "std")]
358    backtrace: Backtrace,
359}
360
361impl<T, const N: usize> TryFromArraySectionError<T, N> {
362    /// Returns the original [`ArraySection`].
363    #[inline]
364    pub fn array_section(self) -> ArraySection<T, N> {
365        self.section
366    }
367
368    #[inline]
369    pub(crate) fn new(section: ArraySection<T, N>) -> Self {
370        Self {
371            section,
372            #[cfg(feature = "std")]
373            backtrace: Backtrace::capture(),
374        }
375    }
376
377    #[cfg(feature = "std")]
378    /// Returns a backtrace to where the error was created.
379    ///
380    /// See [`Backtrace::capture`] for more information about how to make it display more information when printed.
381    #[inline]
382    pub fn backtrace(&self) -> &Backtrace {
383        &self.backtrace
384    }
385}
386
387impl<T, const N: usize> core::fmt::Display for TryFromArraySectionError<T, N> {
388    #[inline]
389    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
390        write!(f, "the array was not fully populated")
391    }
392}
393
394impl<T: core::fmt::Debug, const N: usize> core::error::Error for TryFromArraySectionError<T, N> {}
395
396impl<T, const N: usize> From<TryFromArraySectionError<T, N>> for ArraySection<T, N> {
397    #[inline]
398    fn from(value: TryFromArraySectionError<T, N>) -> Self {
399        value.section
400    }
401}
402
403/// Converts the `ArraySection` into an array if the section is actually the entire array.
404impl<const N: usize, T> TryFrom<ArraySection<T, N>> for [T; N] {
405    type Error = TryFromArraySectionError<T, N>;
406
407    #[inline]
408    fn try_from(value: ArraySection<T, N>) -> Result<Self, Self::Error> {
409        if value.section_is_full_array() {
410            Ok(value.array)
411        } else {
412            Err(TryFromArraySectionError::new(value))
413        }
414    }
415}
416
417// endregion: TryFrom impls
418
419impl<const N: usize, T> From<[T; N]> for ArraySection<T, N> {
420    #[inline]
421    fn from(value: [T; N]) -> Self {
422        Self {
423            start: 0,
424            end: N,
425            array: value,
426        }
427    }
428}
429
430impl<const N: usize, T> AsRef<[T]> for ArraySection<T, N> {
431    #[inline]
432    fn as_ref(&self) -> &[T] {
433        self.as_slice()
434    }
435}
436
437impl<const N: usize, T, I: SliceIndex<[T]>> Index<I> for ArraySection<T, N> {
438    type Output = I::Output;
439    #[inline]
440    fn index(&self, index: I) -> &Self::Output {
441        self.as_slice().index(index)
442    }
443}
444
445// region: PartialEq impls
446
447impl<const N: usize, const M: usize, T, U> PartialEq<ArraySection<T, N>> for ArraySection<U, M>
448where
449    [U]: PartialEq<[T]>,
450{
451    #[inline]
452    fn eq(&self, other: &ArraySection<T, N>) -> bool {
453        self.as_slice().eq(other.as_slice())
454    }
455}
456
457impl<const N: usize, T, U> PartialEq<[U]> for ArraySection<T, N>
458where
459    U: PartialEq<T>,
460{
461    #[inline]
462    fn eq(&self, other: &[U]) -> bool {
463        other == self.as_slice()
464    }
465}
466
467impl<const N: usize, T, U> PartialEq<ArraySection<T, N>> for [U]
468where
469    U: PartialEq<T>,
470{
471    #[inline]
472    fn eq(&self, other: &ArraySection<T, N>) -> bool {
473        self == other.as_slice()
474    }
475}
476
477impl<const N: usize, const M: usize, T, U> PartialEq<[T; N]> for ArraySection<U, M>
478where
479    [U]: PartialEq<[T]>,
480{
481    #[inline]
482    fn eq(&self, other: &[T; N]) -> bool {
483        self.as_slice().eq(other.as_slice())
484    }
485}
486
487impl<const N: usize, const M: usize, T, U> PartialEq<ArraySection<U, M>> for [T; N]
488where
489    [T]: PartialEq<[U]>,
490{
491    #[inline]
492    fn eq(&self, other: &ArraySection<U, M>) -> bool {
493        self.as_slice().eq(other.as_slice())
494    }
495}
496
497// endregion: PartialEq impls
498
499impl<const N: usize, T> IntoIterator for ArraySection<T, N> {
500    type IntoIter = ArraySectionIntoIter<T, N>;
501    type Item = <ArraySectionIntoIter<T, N> as Iterator>::Item;
502    #[inline]
503    fn into_iter(self) -> Self::IntoIter {
504        let start = self.start;
505        let len = self.len();
506        ArraySectionIntoIter::new(self.array.into_iter().skip(start).take(len))
507    }
508}
509
510impl<'a, const N: usize, T> IntoIterator for &'a ArraySection<T, N> {
511    type IntoIter = ArraySectionIter<'a, T>;
512    type Item = <ArraySectionIter<'a, T> as Iterator>::Item;
513    #[inline]
514    fn into_iter(self) -> Self::IntoIter {
515        ArraySectionIter::new(self.as_slice().iter())
516    }
517}
518
519impl<'a, const N: usize, T> IntoIterator for &'a mut ArraySection<T, N> {
520    type IntoIter = ArraySectionIter<'a, T>;
521    type Item = <ArraySectionIter<'a, T> as Iterator>::Item;
522    #[inline]
523    fn into_iter(self) -> Self::IntoIter {
524        ArraySectionIter::new(self.as_slice().iter())
525    }
526}
527
528pub use array_section_iter_mut::ArraySectionIterMut;
529mod array_section_iter_mut {
530    use super::FusedIterator;
531
532    /// Mutable borrowing iterator created by the [`ArraySection::iter_mut()`](super::ArraySection::iter_mut()) function, see it for more information.
533    #[derive(Debug)]
534    pub struct ArraySectionIterMut<'a, T>(core::slice::IterMut<'a, T>);
535
536    impl<'a, T> ArraySectionIterMut<'a, T> {
537        #[inline]
538        pub(crate) const fn new(iter: core::slice::IterMut<'a, T>) -> Self {
539            Self(iter)
540        }
541    }
542
543    impl<'a, T> Iterator for ArraySectionIterMut<'a, T> {
544        type Item = &'a mut T;
545        #[inline]
546        fn next(&mut self) -> Option<Self::Item> {
547            self.0.next()
548        }
549
550        #[inline]
551        fn nth(&mut self, n: usize) -> Option<Self::Item> {
552            self.0.nth(n)
553        }
554
555        #[inline]
556        fn last(self) -> Option<Self::Item> {
557            self.0.last()
558        }
559
560        #[inline]
561        fn count(self) -> usize {
562            self.0.count()
563        }
564
565        #[inline]
566        fn size_hint(&self) -> (usize, Option<usize>) {
567            self.0.size_hint()
568        }
569    }
570
571    impl<'a, T> DoubleEndedIterator for ArraySectionIterMut<'a, T> {
572        #[inline]
573        fn next_back(&mut self) -> Option<Self::Item> {
574            self.0.next_back()
575        }
576
577        #[inline]
578        fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
579            self.0.nth_back(n)
580        }
581    }
582
583    impl<'a, T> ExactSizeIterator for ArraySectionIterMut<'a, T> {
584        #[inline]
585        fn len(&self) -> usize {
586            self.0.len()
587        }
588    }
589
590    impl<'a, T> FusedIterator for ArraySectionIterMut<'a, T> {}
591}
592
593pub use array_section_iter::ArraySectionIter;
594mod array_section_iter {
595    use super::FusedIterator;
596
597    /// Created by the [`iter`](super::ArraySection::iter) function on [`ArraySection`](super::ArraySection), see it for more information.
598    #[derive(Debug, Clone)]
599    #[must_use = "iterators are lazy and do nothing unless consumed"]
600    pub struct ArraySectionIter<'a, T>(core::slice::Iter<'a, T>);
601
602    impl<'a, T> ArraySectionIter<'a, T> {
603        pub(crate) const fn new(iter: core::slice::Iter<'a, T>) -> Self {
604            Self(iter)
605        }
606    }
607
608    impl<'a, T> Iterator for ArraySectionIter<'a, T> {
609        type Item = &'a T;
610        #[inline]
611        fn next(&mut self) -> Option<Self::Item> {
612            self.0.next()
613        }
614
615        #[inline]
616        fn size_hint(&self) -> (usize, Option<usize>) {
617            self.0.size_hint()
618        }
619
620        #[inline]
621        fn last(self) -> Option<Self::Item> {
622            self.0.last()
623        }
624
625        #[inline]
626        fn nth(&mut self, n: usize) -> Option<Self::Item> {
627            self.0.nth(n)
628        }
629
630        #[inline]
631        fn count(self) -> usize {
632            self.0.count()
633        }
634    }
635    impl<'a, T> DoubleEndedIterator for ArraySectionIter<'a, T> {
636        #[inline]
637        fn next_back(&mut self) -> Option<Self::Item> {
638            self.0.next_back()
639        }
640
641        #[inline]
642        fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
643            self.0.nth_back(n)
644        }
645    }
646    impl<'a, T> ExactSizeIterator for ArraySectionIter<'a, T> {
647        fn len(&self) -> usize {
648            self.0.len()
649        }
650    }
651    impl<'a, T> FusedIterator for ArraySectionIter<'a, T> {}
652}
653
654pub use array_section_into_iter::ArraySectionIntoIter;
655mod array_section_into_iter {
656    use super::FusedIterator;
657
658    #[derive(Debug, Clone)]
659    /// Created by the [`into_iter`](super::ArraySection::into_iter) function on [`ArraySection`](super::ArraySection), see it for more information.
660    #[must_use = "iterators are lazy and do nothing unless consumed"]
661    pub struct ArraySectionIntoIter<T, const N: usize>(
662        core::iter::Take<core::iter::Skip<core::array::IntoIter<T, N>>>,
663    );
664
665    impl<const N: usize, T> ArraySectionIntoIter<T, N> {
666        pub(crate) const fn new(
667            iter: core::iter::Take<core::iter::Skip<core::array::IntoIter<T, N>>>,
668        ) -> Self {
669            Self(iter)
670        }
671    }
672
673    impl<const N: usize, T> Iterator for ArraySectionIntoIter<T, N> {
674        type Item = T;
675        #[inline]
676        fn next(&mut self) -> Option<Self::Item> {
677            self.0.next()
678        }
679
680        #[inline]
681        fn size_hint(&self) -> (usize, Option<usize>) {
682            let l = self.0.len();
683            (l, Some(l))
684        }
685
686        #[inline]
687        fn nth(&mut self, index: usize) -> Option<Self::Item> {
688            self.0.nth(index)
689        }
690
691        #[inline]
692        fn last(self) -> Option<T> {
693            self.0.last()
694        }
695
696        #[inline]
697        fn count(self) -> usize {
698            self.0.count()
699        }
700    }
701    impl<const N: usize, T> FusedIterator for ArraySectionIntoIter<T, N> {}
702    impl<const N: usize, T> ExactSizeIterator for ArraySectionIntoIter<T, N> {}
703    impl<const N: usize, T> DoubleEndedIterator for ArraySectionIntoIter<T, N> {
704        #[inline]
705        fn next_back(&mut self) -> Option<Self::Item> {
706            self.0.next_back()
707        }
708
709        #[inline]
710        fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
711            self.0.nth_back(n)
712        }
713    }
714}