nonempty_collections/
array.rs

1//! Extends non-zero length arrays with conversion methods to non-empty
2//! collections.
3//!
4//! Since fixed-size arrays are by definition already not empty, they aren't
5//! given a special wrapper type like [`crate::NEVec`]. Instead, we enable them
6//! to be easily iterated over in a compatible way:
7//!
8//! ```
9//! use nonempty_collections::*;
10//!
11//! let a: [u32; 4] = [1, 2, 3, 4];
12//! let v: NEVec<_> = a.into_nonempty_iter().map(|n| n + 1).collect();
13//! assert_eq!(nev![2, 3, 4, 5], v);
14//! ```
15//!
16//! See [`NonEmptyArrayExt`] for more conversions.
17//!
18//! # Caveats
19//!
20//! These extensions are only provided for arrays up to size 32.
21
22use core::fmt;
23use std::num::NonZeroUsize;
24
25use crate::impl_nonempty_iter_for_arrays;
26use crate::IntoNonEmptyIterator;
27use crate::NonEmptyIterator;
28
29/// Provides extension methods for non-empty arrays.
30///
31/// # Examples
32///
33/// Create a non-empty slice of an array:
34///
35/// ```
36/// # use nonempty_collections::*;
37/// assert_eq!(
38///     NESlice::try_from_slice(&[1, 2]),
39///     Some([1, 2].as_nonempty_slice())
40/// );
41/// ```
42///
43/// Get the length of an array as a [`NonZeroUsize`]:
44///
45/// ```
46/// # use nonempty_collections::NonEmptyArrayExt;
47/// # use std::num::NonZeroUsize;
48/// assert_eq!(NonZeroUsize::MIN, [1].nonzero_len());
49/// ```
50///
51/// Convert array into a non-empty vec:
52///
53/// ```
54/// # use nonempty_collections::*;
55/// assert_eq!(nev![4], [4].into_nonempty_vec());
56/// ```
57pub trait NonEmptyArrayExt<T> {
58    /// Create a `NESlice` that borrows the contents of `self`.
59    fn as_nonempty_slice(&self) -> crate::NESlice<'_, T>;
60
61    /// Returns the length of this array as a [`NonZeroUsize`].
62    fn nonzero_len(&self) -> NonZeroUsize;
63
64    /// Moves `self` into a new [`crate::NEVec`].
65    fn into_nonempty_vec(self) -> crate::NEVec<T>;
66}
67
68/// Non-empty iterator for arrays with length > 0.
69///
70/// # Examples
71///
72/// Use non-zero length arrays anywhere an [`IntoNonEmptyIterator`] is expected.
73///
74/// ```
75/// use std::num::NonZeroUsize;
76///
77/// use nonempty_collections::*;
78///
79/// fn is_one<T>(iter: impl IntoNonEmptyIterator<Item = T>) {
80///     assert_eq!(NonZeroUsize::MIN, iter.into_nonempty_iter().count());
81/// }
82///
83/// is_one([0]);
84/// ```
85///
86/// Only compiles for non-empty arrays:
87///
88/// ```compile_fail
89/// use nonempty_collections::*;
90///
91/// fn is_one(iter: impl IntoNonEmptyIterator<Item = usize>) {}
92///
93/// is_one([]); // Doesn't compile because it is empty.
94/// ```
95#[derive(Clone)]
96pub struct ArrayNonEmptyIterator<T, const C: usize> {
97    iter: core::array::IntoIter<T, C>,
98}
99
100impl<T, const C: usize> IntoIterator for ArrayNonEmptyIterator<T, C> {
101    type Item = T;
102
103    type IntoIter = core::array::IntoIter<T, C>;
104
105    fn into_iter(self) -> Self::IntoIter {
106        self.iter
107    }
108}
109
110impl<T, const C: usize> NonEmptyIterator for ArrayNonEmptyIterator<T, C> {}
111
112impl<T: fmt::Debug, const C: usize> fmt::Debug for ArrayNonEmptyIterator<T, C> {
113    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114        self.iter.fmt(f)
115    }
116}
117
118// NOTE 2024-04-05 This must never be implemented for 0.
119//
120// Also, happy birthday Dad.
121impl_nonempty_iter_for_arrays!(
122    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,
123    27, 28, 29, 30, 31, 32
124);
125
126#[doc(hidden)]
127#[macro_export]
128macro_rules! impl_nonempty_iter_for_arrays {
129    ($($i:literal),+ $(,)?) => {
130        $(
131            impl<T> IntoNonEmptyIterator for [T; $i] {
132                type IntoNEIter = ArrayNonEmptyIterator<T, $i>;
133
134                fn into_nonempty_iter(self) -> Self::IntoNEIter {
135                    ArrayNonEmptyIterator {
136                        iter: self.into_iter(),
137                    }
138                }
139            }
140
141            impl<'a, T> IntoNonEmptyIterator for &'a [T; $i] {
142                type IntoNEIter = $crate::slice::Iter<'a,T>;
143
144                fn into_nonempty_iter(self) -> Self::IntoNEIter {
145                    self.as_nonempty_slice().into_nonempty_iter()
146                }
147            }
148
149            impl<T> NonEmptyArrayExt<T> for [T; $i] {
150                fn as_nonempty_slice(&self) -> $crate::NESlice<'_, T> {
151                    // This should never panic because a slice with length > 0
152                    // is non-empty by definition.
153                    $crate::NESlice::try_from_slice(self).unwrap()
154                }
155
156                fn nonzero_len(&self) -> NonZeroUsize {
157                    // This should be fine because $i is always > 0.
158                    unsafe { NonZeroUsize::new_unchecked($i) }
159                }
160
161                fn into_nonempty_vec(self) -> $crate::NEVec<T> {
162                    self.into_nonempty_iter().collect()
163                }
164            }
165        )+
166    };
167}
168
169#[cfg(test)]
170mod test {
171    use crate::IntoNonEmptyIterator;
172    use crate::NonEmptyIterator;
173
174    #[test]
175    fn test_iter() {
176        let iter = [1, 2, 3, 4].into_nonempty_iter();
177        let (first, rest) = iter.next();
178        assert_eq!(1, first);
179        assert_eq!(vec![2, 3, 4], rest.into_iter().collect::<Vec<_>>());
180
181        let iter = [1].into_nonempty_iter();
182        let (first, rest) = iter.next();
183        assert_eq!(1, first);
184        assert_eq!(0, rest.into_iter().count());
185
186        assert_eq!(33, [1, -2, 33, 4].into_nonempty_iter().max());
187    }
188
189    #[test]
190    fn test_iter_ref() {
191        let iter = (&[1, 2, 3, 4]).into_nonempty_iter();
192        let (first, rest) = iter.next();
193        assert_eq!(&1, first);
194        assert_eq!(vec![&2, &3, &4], rest.into_iter().collect::<Vec<_>>());
195
196        let iter = (&[1]).into_nonempty_iter();
197        let (first, rest) = iter.next();
198        assert_eq!(&1, first);
199        assert_eq!(0, rest.into_iter().count());
200
201        assert_eq!(&33, (&[1, -2, 33, 4]).into_nonempty_iter().max());
202    }
203}