bounded_vec/
bounded_vec.rs

1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3use std::convert::{TryFrom, TryInto};
4use std::slice::{Iter, IterMut};
5use std::vec;
6use thiserror::Error;
7
8/// Non-empty Vec bounded with minimal (L - lower bound) and maximal (U - upper bound) items quantity
9#[derive(PartialEq, Eq, Debug, Clone, Hash, PartialOrd, Ord)]
10#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
11pub struct BoundedVec<T, const L: usize, const U: usize>
12// enable when feature(const_evaluatable_checked) is stable
13// where
14//     Assert<{ L > 0 }>: IsTrue,
15{
16    inner: Vec<T>,
17}
18
19// enum Assert<const COND: bool> {}
20
21// trait IsTrue {}
22
23// impl IsTrue for Assert<true> {}
24
25/// BoundedVec errors
26#[derive(Error, PartialEq, Eq, Debug, Clone)]
27pub enum BoundedVecOutOfBounds {
28    /// Items quantity is less than L (lower bound)
29    #[error("Lower bound violation: got {got} (expected >= {lower_bound})")]
30    LowerBoundError {
31        /// L (lower bound)
32        lower_bound: usize,
33        /// provided value
34        got: usize,
35    },
36    /// Items quantity is more than U (upper bound)
37    #[error("Upper bound violation: got {got} (expected <= {upper_bound})")]
38    UpperBoundError {
39        /// U (upper bound)
40        upper_bound: usize,
41        /// provided value
42        got: usize,
43    },
44}
45
46impl<T, const L: usize, const U: usize> BoundedVec<T, L, U> {
47    /// Creates new BoundedVec or returns error if items count is out of bounds
48    ///
49    /// # Example
50    /// ```
51    /// use bounded_vec::BoundedVec;
52    /// let data: BoundedVec<_, 2, 8> = BoundedVec::from_vec(vec![1u8, 2]).unwrap();
53    /// ```
54    pub fn from_vec(items: Vec<T>) -> Result<Self, BoundedVecOutOfBounds> {
55        // remove when feature(const_evaluatable_checked) is stable
56        // and this requirement is encoded in type sig
57        assert!(L > 0);
58        let len = items.len();
59        if len < L {
60            Err(BoundedVecOutOfBounds::LowerBoundError {
61                lower_bound: L,
62                got: len,
63            })
64        } else if len > U {
65            Err(BoundedVecOutOfBounds::UpperBoundError {
66                upper_bound: U,
67                got: len,
68            })
69        } else {
70            Ok(BoundedVec { inner: items })
71        }
72    }
73
74    /// Returns a reference to underlying `Vec``
75    ///
76    /// # Example
77    /// ```
78    /// use bounded_vec::BoundedVec;
79    /// use std::convert::TryInto;
80    ///
81    /// let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap();
82    /// assert_eq!(data.as_vec(), &vec![1u8,2]);
83    /// ```
84    pub fn as_vec(&self) -> &Vec<T> {
85        &self.inner
86    }
87
88    /// Returns an underlying `Vec``
89    ///
90    /// # Example
91    /// ```
92    /// use bounded_vec::BoundedVec;
93    /// use std::convert::TryInto;
94    ///
95    /// let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap();
96    /// assert_eq!(data.to_vec(), vec![1u8,2]);
97    /// ```
98    pub fn to_vec(self) -> Vec<T> {
99        self.into()
100    }
101
102    /// Returns the number of elements in the vector
103    ///
104    /// # Example
105    /// ```
106    /// use bounded_vec::BoundedVec;
107    /// use std::convert::TryInto;
108    ///
109    /// let data: BoundedVec<u8, 2, 4> = vec![1u8,2].try_into().unwrap();
110    /// assert_eq!(data.len(), 2);
111    /// ```
112    pub fn len(&self) -> usize {
113        self.inner.len()
114    }
115
116    /// Always returns `false` (cannot be empty)
117    ///
118    /// # Example
119    /// ```
120    /// use bounded_vec::BoundedVec;
121    /// use std::convert::TryInto;
122    ///
123    /// let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap();
124    /// assert_eq!(data.is_empty(), false);
125    /// ```
126    pub fn is_empty(&self) -> bool {
127        false
128    }
129
130    /// Extracts a slice containing the entire vector.
131    ///
132    /// # Example
133    /// ```
134    /// use bounded_vec::BoundedVec;
135    /// use std::convert::TryInto;
136    ///
137    /// let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap();
138    /// assert_eq!(data.as_slice(), &[1u8,2]);
139    /// ```
140    pub fn as_slice(&self) -> &[T] {
141        self.inner.as_slice()
142    }
143
144    /// Returns the first element of non-empty Vec
145    ///
146    /// # Example
147    /// ```
148    /// use bounded_vec::BoundedVec;
149    /// use std::convert::TryInto;
150    ///
151    /// let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap();
152    /// assert_eq!(*data.first(), 1);
153    /// ```
154    pub fn first(&self) -> &T {
155        #[allow(clippy::unwrap_used)]
156        self.inner.first().unwrap()
157    }
158
159    /// Returns the last element of non-empty Vec
160    ///
161    /// # Example
162    /// ```
163    /// use bounded_vec::BoundedVec;
164    /// use std::convert::TryInto;
165    ///
166    /// let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap();
167    /// assert_eq!(*data.last(), 2);
168    /// ```
169    pub fn last(&self) -> &T {
170        #[allow(clippy::unwrap_used)]
171        self.inner.last().unwrap()
172    }
173
174    /// Create a new `BoundedVec` by consuming `self` and mapping each element.
175    ///
176    /// This is useful as it keeps the knowledge that the length is >= U, <= L,
177    /// even through the old `BoundedVec` is consumed and turned into an iterator.
178    ///
179    /// # Example
180    ///
181    /// ```
182    /// use bounded_vec::BoundedVec;
183    /// let data: BoundedVec<u8, 2, 8> = [1u8,2].into();
184    /// let data = data.mapped(|x|x*2);
185    /// assert_eq!(data, [2u8,4].into());
186    /// ```
187    pub fn mapped<F, N>(self, map_fn: F) -> BoundedVec<N, L, U>
188    where
189        F: FnMut(T) -> N,
190    {
191        BoundedVec {
192            inner: self.inner.into_iter().map(map_fn).collect::<Vec<_>>(),
193        }
194    }
195
196    /// Create a new `BoundedVec` by mapping references to the elements of self
197    ///
198    /// This is useful as it keeps the knowledge that the length is >= U, <= L,
199    /// will still hold for new `BoundedVec`
200    ///
201    /// # Example
202    ///
203    /// ```
204    /// use bounded_vec::BoundedVec;
205    /// let data: BoundedVec<u8, 2, 8> = [1u8,2].into();
206    /// let data = data.mapped_ref(|x|x*2);
207    /// assert_eq!(data, [2u8,4].into());
208    /// ```
209    pub fn mapped_ref<F, N>(&self, map_fn: F) -> BoundedVec<N, L, U>
210    where
211        F: FnMut(&T) -> N,
212    {
213        BoundedVec {
214            inner: self.inner.iter().map(map_fn).collect::<Vec<_>>(),
215        }
216    }
217
218    /// Create a new `BoundedVec` by consuming `self` and mapping each element
219    /// to a `Result`.
220    ///
221    /// This is useful as it keeps the knowledge that the length is preserved
222    /// even through the old `BoundedVec` is consumed and turned into an iterator.
223    ///
224    /// As this method consumes self, returning an error means that this
225    /// vec is dropped. I.e. this method behaves roughly like using a
226    /// chain of `into_iter()`, `map`, `collect::<Result<Vec<N>,E>>` and
227    /// then converting the `Vec` back to a `Vec1`.
228    ///
229    ///
230    /// # Errors
231    ///
232    /// Once any call to `map_fn` returns a error that error is directly
233    /// returned by this method.
234    ///
235    /// # Example
236    ///
237    /// ```
238    /// use bounded_vec::BoundedVec;
239    /// let data: BoundedVec<u8, 2, 8> = [1u8,2].into();
240    /// let data: Result<BoundedVec<u8, 2, 8>, _> = data.try_mapped(|x| Err("failed"));
241    /// assert_eq!(data, Err("failed"));
242    /// ```
243    pub fn try_mapped<F, N, E>(self, map_fn: F) -> Result<BoundedVec<N, L, U>, E>
244    where
245        F: FnMut(T) -> Result<N, E>,
246    {
247        let mut map_fn = map_fn;
248        let mut out = Vec::with_capacity(self.len());
249        for element in self.inner.into_iter() {
250            out.push(map_fn(element)?);
251        }
252        #[allow(clippy::unwrap_used)]
253        Ok(BoundedVec::from_vec(out).unwrap())
254    }
255
256    /// Create a new `BoundedVec` by mapping references of `self` elements
257    /// to a `Result`.
258    ///
259    /// This is useful as it keeps the knowledge that the length is preserved
260    /// even through the old `BoundedVec` is consumed and turned into an iterator.
261    ///
262    /// # Errors
263    ///
264    /// Once any call to `map_fn` returns a error that error is directly
265    /// returned by this method.
266    ///
267    /// # Example
268    ///
269    /// ```
270    /// use bounded_vec::BoundedVec;
271    /// let data: BoundedVec<u8, 2, 8> = [1u8,2].into();
272    /// let data: Result<BoundedVec<u8, 2, 8>, _> = data.try_mapped_ref(|x| Err("failed"));
273    /// assert_eq!(data, Err("failed"));
274    /// ```
275    pub fn try_mapped_ref<F, N, E>(&self, map_fn: F) -> Result<BoundedVec<N, L, U>, E>
276    where
277        F: FnMut(&T) -> Result<N, E>,
278    {
279        let mut map_fn = map_fn;
280        let mut out = Vec::with_capacity(self.len());
281        for element in self.inner.iter() {
282            out.push(map_fn(element)?);
283        }
284        #[allow(clippy::unwrap_used)]
285        Ok(BoundedVec::from_vec(out).unwrap())
286    }
287
288    /// Returns a reference for an element at index or `None` if out of bounds
289    ///
290    /// # Example
291    ///
292    /// ```
293    /// use bounded_vec::BoundedVec;
294    /// let data: BoundedVec<u8, 2, 8> = [1u8,2].into();
295    /// let elem = *data.get(1).unwrap();
296    /// assert_eq!(elem, 2);
297    /// ```
298    pub fn get(&self, index: usize) -> Option<&T> {
299        self.inner.get(index)
300    }
301
302    /// Returns an iterator
303    pub fn iter(&self) -> Iter<T> {
304        self.inner.iter()
305    }
306
307    /// Returns an iterator that allows to modify each value
308    pub fn iter_mut(&mut self) -> IterMut<T> {
309        self.inner.iter_mut()
310    }
311
312    /// Returns the last and all the rest of the elements
313    pub fn split_last(&self) -> (&T, &[T]) {
314        #[allow(clippy::unwrap_used)]
315        self.inner.split_last().unwrap()
316    }
317
318    /// Return a new BoundedVec with indices included
319    pub fn enumerated(self) -> BoundedVec<(usize, T), L, U> {
320        #[allow(clippy::unwrap_used)]
321        self.inner
322            .into_iter()
323            .enumerate()
324            .collect::<Vec<(usize, T)>>()
325            .try_into()
326            .unwrap()
327    }
328
329    /// Return a Some(BoundedVec) or None if `v` is empty
330    /// # Example
331    /// ```
332    /// use bounded_vec::BoundedVec;
333    /// use bounded_vec::OptBoundedVecToVec;
334    ///
335    /// let opt_bv_none = BoundedVec::<u8, 2, 8>::opt_empty_vec(vec![]).unwrap();
336    /// assert!(opt_bv_none.is_none());
337    /// assert_eq!(opt_bv_none.to_vec(), vec![]);
338    /// let opt_bv_some = BoundedVec::<u8, 2, 8>::opt_empty_vec(vec![0u8, 2]).unwrap();
339    /// assert!(opt_bv_some.is_some());
340    /// assert_eq!(opt_bv_some.to_vec(), vec![0u8, 2]);
341    /// ```
342    pub fn opt_empty_vec(v: Vec<T>) -> Result<Option<BoundedVec<T, L, U>>, BoundedVecOutOfBounds> {
343        if v.is_empty() {
344            Ok(None)
345        } else {
346            Ok(Some(BoundedVec::from_vec(v)?))
347        }
348    }
349}
350
351/// A non-empty Vec with no effective upper-bound on its length
352pub type NonEmptyVec<T> = BoundedVec<T, 1, { usize::MAX }>;
353
354impl<T, const L: usize, const U: usize> TryFrom<Vec<T>> for BoundedVec<T, L, U> {
355    type Error = BoundedVecOutOfBounds;
356
357    fn try_from(value: Vec<T>) -> Result<Self, Self::Error> {
358        BoundedVec::from_vec(value)
359    }
360}
361
362// when feature(const_evaluatable_checked) is stable cover all array sizes (L..=U)
363impl<T, const L: usize, const U: usize> From<[T; L]> for BoundedVec<T, L, U> {
364    fn from(arr: [T; L]) -> Self {
365        BoundedVec { inner: arr.into() }
366    }
367}
368
369impl<T, const L: usize, const U: usize> From<BoundedVec<T, L, U>> for Vec<T> {
370    fn from(v: BoundedVec<T, L, U>) -> Self {
371        v.inner
372    }
373}
374
375impl<T, const L: usize, const U: usize> IntoIterator for BoundedVec<T, L, U> {
376    type Item = T;
377    type IntoIter = vec::IntoIter<T>;
378
379    fn into_iter(self) -> Self::IntoIter {
380        self.inner.into_iter()
381    }
382}
383
384impl<'a, T, const L: usize, const U: usize> IntoIterator for &'a BoundedVec<T, L, U> {
385    type Item = &'a T;
386    type IntoIter = core::slice::Iter<'a, T>;
387
388    fn into_iter(self) -> Self::IntoIter {
389        (&self.inner).iter()
390    }
391}
392
393impl<'a, T, const L: usize, const U: usize> IntoIterator for &'a mut BoundedVec<T, L, U> {
394    type Item = &'a mut T;
395    type IntoIter = core::slice::IterMut<'a, T>;
396
397    fn into_iter(self) -> Self::IntoIter {
398        (&mut self.inner).iter_mut()
399    }
400}
401
402impl<T, const L: usize, const U: usize> AsRef<Vec<T>> for BoundedVec<T, L, U> {
403    fn as_ref(&self) -> &Vec<T> {
404        &self.inner
405    }
406}
407
408impl<T, const L: usize, const U: usize> AsRef<[T]> for BoundedVec<T, L, U> {
409    fn as_ref(&self) -> &[T] {
410        self.inner.as_ref()
411    }
412}
413
414impl<T, const L: usize, const U: usize> AsMut<Vec<T>> for BoundedVec<T, L, U> {
415    fn as_mut(&mut self) -> &mut Vec<T> {
416        self.inner.as_mut()
417    }
418}
419
420impl<T, const L: usize, const U: usize> AsMut<[T]> for BoundedVec<T, L, U> {
421    fn as_mut(&mut self) -> &mut [T] {
422        self.inner.as_mut()
423    }
424}
425
426/// Option<BoundedVec<T, _, _>> to Vec<T>
427pub trait OptBoundedVecToVec<T> {
428    /// Option<BoundedVec<T, _, _>> to Vec<T>
429    fn to_vec(self) -> Vec<T>;
430}
431
432impl<T, const L: usize, const U: usize> OptBoundedVecToVec<T> for Option<BoundedVec<T, L, U>> {
433    fn to_vec(self) -> Vec<T> {
434        self.map(|bv| bv.into()).unwrap_or_default()
435    }
436}
437
438#[allow(clippy::unwrap_used)]
439#[cfg(feature = "arbitrary")]
440mod arbitrary {
441
442    use super::*;
443    use proptest::collection::vec;
444    use proptest::prelude::Arbitrary;
445    use proptest::prelude::*;
446    use proptest::strategy::BoxedStrategy;
447
448    impl<T: Arbitrary, const L: usize, const U: usize> Arbitrary for BoundedVec<T, L, U>
449    where
450        T::Strategy: 'static,
451    {
452        type Strategy = BoxedStrategy<Self>;
453        type Parameters = ();
454
455        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
456            vec(any::<T>(), L..=U)
457                .prop_map(|items| BoundedVec::from_vec(items).unwrap())
458                .boxed()
459        }
460    }
461}
462
463#[allow(clippy::unwrap_used)]
464#[cfg(test)]
465mod tests {
466    use std::convert::TryInto;
467
468    use super::*;
469
470    #[test]
471    fn from_vec() {
472        assert!(BoundedVec::<u8, 2, 8>::from_vec(vec![1, 2]).is_ok());
473        assert!(BoundedVec::<u8, 2, 8>::from_vec(vec![]).is_err());
474        assert!(BoundedVec::<u8, 3, 8>::from_vec(vec![1, 2]).is_err());
475        assert!(BoundedVec::<u8, 1, 2>::from_vec(vec![1, 2, 3]).is_err());
476    }
477
478    #[test]
479    fn is_empty() {
480        let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap();
481        assert!(!data.is_empty());
482    }
483
484    #[test]
485    fn as_vec() {
486        let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap();
487        assert_eq!(data.as_vec(), &vec![1u8, 2]);
488    }
489
490    #[test]
491    fn as_slice() {
492        let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap();
493        assert_eq!(data.as_slice(), &[1u8, 2]);
494    }
495
496    #[test]
497    fn len() {
498        let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap();
499        assert_eq!(data.len(), 2);
500    }
501
502    #[test]
503    fn first() {
504        let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap();
505        assert_eq!(data.first(), &1u8);
506    }
507
508    #[test]
509    fn last() {
510        let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap();
511        assert_eq!(data.last(), &2u8);
512    }
513
514    #[test]
515    fn mapped() {
516        let data: BoundedVec<u8, 2, 8> = [1u8, 2].into();
517        let data = data.mapped(|x| x * 2);
518        assert_eq!(data, [2u8, 4].into());
519    }
520
521    #[test]
522    fn mapped_ref() {
523        let data: BoundedVec<u8, 2, 8> = [1u8, 2].into();
524        let data = data.mapped_ref(|x| x * 2);
525        assert_eq!(data, [2u8, 4].into());
526    }
527
528    #[test]
529    fn get() {
530        let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap();
531        assert_eq!(data.get(1).unwrap(), &2u8);
532        assert!(data.get(3).is_none());
533    }
534
535    #[test]
536    fn try_mapped() {
537        let data: BoundedVec<u8, 2, 8> = [1u8, 2].into();
538        let data = data.try_mapped(|x| 100u8.checked_div(x).ok_or("error"));
539        assert_eq!(data, Ok([100u8, 50].into()));
540    }
541
542    #[test]
543    fn try_mapped_error() {
544        let data: BoundedVec<u8, 2, 8> = [0u8, 2].into();
545        let data = data.try_mapped(|x| 100u8.checked_div(x).ok_or("error"));
546        assert_eq!(data, Err("error"));
547    }
548
549    #[test]
550    fn try_mapped_ref() {
551        let data: BoundedVec<u8, 2, 8> = [1u8, 2].into();
552        let data = data.try_mapped_ref(|x| 100u8.checked_div(*x).ok_or("error"));
553        assert_eq!(data, Ok([100u8, 50].into()));
554    }
555
556    #[test]
557    fn try_mapped_ref_error() {
558        let data: BoundedVec<u8, 2, 8> = [0u8, 2].into();
559        let data = data.try_mapped_ref(|x| 100u8.checked_div(*x).ok_or("error"));
560        assert_eq!(data, Err("error"));
561    }
562
563    #[test]
564    fn split_last() {
565        let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap();
566        assert_eq!(data.split_last(), (&2u8, [1u8].as_ref()));
567        let data1: BoundedVec<_, 1, 8> = vec![1u8].try_into().unwrap();
568        assert_eq!(data1.split_last(), (&1u8, Vec::new().as_ref()));
569    }
570
571    #[test]
572    fn enumerated() {
573        let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap();
574        let expected: BoundedVec<_, 2, 8> = vec![(0, 1u8), (1, 2)].try_into().unwrap();
575        assert_eq!(data.enumerated(), expected);
576    }
577
578    #[test]
579    fn into_iter() {
580        let mut vec = vec![1u8, 2];
581        let mut data: BoundedVec<_, 2, 8> = vec.clone().try_into().unwrap();
582        assert_eq!(data.clone().into_iter().collect::<Vec<u8>>(), vec);
583        assert_eq!(
584            data.iter().collect::<Vec<&u8>>(),
585            vec.iter().collect::<Vec<&u8>>()
586        );
587        assert_eq!(
588            data.iter_mut().collect::<Vec<&mut u8>>(),
589            vec.iter_mut().collect::<Vec<&mut u8>>()
590        );
591    }
592}
593
594#[cfg(feature = "arbitrary")]
595#[cfg(test)]
596#[allow(clippy::len_zero)]
597mod arb_tests {
598
599    use super::*;
600    use proptest::prelude::*;
601
602    proptest! {
603
604        #[test]
605        fn bounded_vec_length_bounded(v: BoundedVec<u8, 1, 2>) {
606            prop_assert!(1 <= v.len() && v.len() <= 2);
607        }
608    }
609}