From f67d89b09d6fdff8f8f171d9d65b33f1550a515d Mon Sep 17 00:00:00 2001 From: fili_pk Date: Mon, 8 Apr 2024 22:16:26 +0200 Subject: [PATCH 1/2] Removed non-empty restriction --- README.md | 2 +- src/bounded_vec.rs | 59 +++++++++++++--------------------------------- 2 files changed, 17 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index b6731a9..d3fb0ea 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Latest Version](https://img.shields.io/crates/v/bounded-vec.svg)](https://crates.io/crates/bounded-vec) [![Documentation](https://docs.rs/bounded-vec/badge.svg)](https://docs.rs/crate/bounded-vec) ## bounded-vec -`BoundedVec` - Non-empty rust `std::vec::Vec` wrapper with type guarantees on lower(`L`) and upper(`U`) bounds for items quantity. Inspired by [vec1](https://github.com/rustonaut/vec1). +`BoundedVec` - rust `std::vec::Vec` wrapper with type guarantees on lower(`L`) and upper(`U`) bounds for items quantity. Inspired by [vec1](https://github.com/rustonaut/vec1). ## Example diff --git a/src/bounded_vec.rs b/src/bounded_vec.rs index 35a9573..4f6b3df 100644 --- a/src/bounded_vec.rs +++ b/src/bounded_vec.rs @@ -5,7 +5,7 @@ use std::slice::{Iter, IterMut}; use std::vec; use thiserror::Error; -/// Non-empty Vec bounded with minimal (L - lower bound) and maximal (U - upper bound) items quantity +/// Bounded Vec with minimal (L - lower bound) and maximal (U - upper bound) items quantity #[derive(PartialEq, Eq, Debug, Clone, Hash, PartialOrd, Ord)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))] pub struct BoundedVec @@ -52,9 +52,6 @@ impl BoundedVec { /// let data: BoundedVec<_, 2, 8> = BoundedVec::from_vec(vec![1u8, 2]).unwrap(); /// ``` pub fn from_vec(items: Vec) -> Result { - // remove when feature(const_evaluatable_checked) is stable - // and this requirement is encoded in type sig - assert!(L > 0); let len = items.len(); if len < L { Err(BoundedVecOutOfBounds::LowerBoundError { @@ -124,7 +121,7 @@ impl BoundedVec { /// assert_eq!(data.is_empty(), false); /// ``` pub fn is_empty(&self) -> bool { - false + self.inner.is_empty() } /// Extracts a slice containing the entire vector. @@ -141,7 +138,7 @@ impl BoundedVec { self.inner.as_slice() } - /// Returns the first element of non-empty Vec + /// Returns the first element of Vec /// /// # Example /// ``` @@ -149,14 +146,13 @@ impl BoundedVec { /// use std::convert::TryInto; /// /// let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap(); - /// assert_eq!(*data.first(), 1); + /// assert_eq!(data.first(), Some(&1)); /// ``` - pub fn first(&self) -> &T { - #[allow(clippy::unwrap_used)] - self.inner.first().unwrap() + pub fn first(&self) -> Option<&T> { + self.inner.first() } - /// Returns the last element of non-empty Vec + /// Returns the last element of Vec /// /// # Example /// ``` @@ -164,11 +160,10 @@ impl BoundedVec { /// use std::convert::TryInto; /// /// let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap(); - /// assert_eq!(*data.last(), 2); + /// assert_eq!(data.last(), Some(&2)); /// ``` - pub fn last(&self) -> &T { - #[allow(clippy::unwrap_used)] - self.inner.last().unwrap() + pub fn last(&self) -> Option<&T> { + self.inner.last() } /// Create a new `BoundedVec` by consuming `self` and mapping each element. @@ -310,9 +305,8 @@ impl BoundedVec { } /// Returns the last and all the rest of the elements - pub fn split_last(&self) -> (&T, &[T]) { - #[allow(clippy::unwrap_used)] - self.inner.split_last().unwrap() + pub fn split_last(&self) -> Option<(&T, &[T])> { + self.inner.split_last() } /// Return a new BoundedVec with indices included @@ -325,27 +319,6 @@ impl BoundedVec { .try_into() .unwrap() } - - /// Return a Some(BoundedVec) or None if `v` is empty - /// # Example - /// ``` - /// use bounded_vec::BoundedVec; - /// use bounded_vec::OptBoundedVecToVec; - /// - /// let opt_bv_none = BoundedVec::::opt_empty_vec(vec![]).unwrap(); - /// assert!(opt_bv_none.is_none()); - /// assert_eq!(opt_bv_none.to_vec(), vec![]); - /// let opt_bv_some = BoundedVec::::opt_empty_vec(vec![0u8, 2]).unwrap(); - /// assert!(opt_bv_some.is_some()); - /// assert_eq!(opt_bv_some.to_vec(), vec![0u8, 2]); - /// ``` - pub fn opt_empty_vec(v: Vec) -> Result>, BoundedVecOutOfBounds> { - if v.is_empty() { - Ok(None) - } else { - Ok(Some(BoundedVec::from_vec(v)?)) - } - } } /// A non-empty Vec with no effective upper-bound on its length @@ -502,13 +475,13 @@ mod tests { #[test] fn first() { let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap(); - assert_eq!(data.first(), &1u8); + assert_eq!(data.first(), Some(&1u8)); } #[test] fn last() { let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap(); - assert_eq!(data.last(), &2u8); + assert_eq!(data.last(), Some(&2u8)); } #[test] @@ -563,9 +536,9 @@ mod tests { #[test] fn split_last() { let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap(); - assert_eq!(data.split_last(), (&2u8, [1u8].as_ref())); + assert_eq!(data.split_last(), Some((&2u8, [1u8].as_ref()))); let data1: BoundedVec<_, 1, 8> = vec![1u8].try_into().unwrap(); - assert_eq!(data1.split_last(), (&1u8, Vec::new().as_ref())); + assert_eq!(data1.split_last(), Some((&1u8, Vec::new().as_ref()))); } #[test] From 3457ee84c6bac7a0cc8f81a6bc2f68805f46e0c8 Mon Sep 17 00:00:00 2001 From: fili_pk Date: Fri, 9 May 2025 08:03:35 +0200 Subject: [PATCH 2/2] Fixed backward compatibility problem --- src/bounded_vec.rs | 120 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 104 insertions(+), 16 deletions(-) diff --git a/src/bounded_vec.rs b/src/bounded_vec.rs index 4f6b3df..9a64447 100644 --- a/src/bounded_vec.rs +++ b/src/bounded_vec.rs @@ -138,7 +138,7 @@ impl BoundedVec { self.inner.as_slice() } - /// Returns the first element of Vec + /// Returns the first element of non-empty Vec /// /// # Example /// ``` @@ -146,13 +146,15 @@ impl BoundedVec { /// use std::convert::TryInto; /// /// let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap(); - /// assert_eq!(data.first(), Some(&1)); + /// assert_eq!(*data.first(), 1); /// ``` - pub fn first(&self) -> Option<&T> { - self.inner.first() + pub fn first(&self) -> &T { + const { assert!(L != 0) } + #[allow(clippy::unwrap_used)] + self.inner.first().unwrap() } - /// Returns the last element of Vec + // Returns the last element of Vec /// /// # Example /// ``` @@ -160,10 +162,12 @@ impl BoundedVec { /// use std::convert::TryInto; /// /// let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap(); - /// assert_eq!(data.last(), Some(&2)); + /// assert_eq!(*data.last(), 2); /// ``` - pub fn last(&self) -> Option<&T> { - self.inner.last() + pub fn last(&self) -> &T { + const { assert!(L != 0) } + #[allow(clippy::unwrap_used)] + self.inner.last().unwrap() } /// Create a new `BoundedVec` by consuming `self` and mapping each element. @@ -305,8 +309,10 @@ impl BoundedVec { } /// Returns the last and all the rest of the elements - pub fn split_last(&self) -> Option<(&T, &[T])> { - self.inner.split_last() + pub fn split_last(&self) -> (&T, &[T]) { + const { assert!(L != 0) } + #[allow(clippy::unwrap_used)] + self.inner.split_last().unwrap() } /// Return a new BoundedVec with indices included @@ -319,6 +325,62 @@ impl BoundedVec { .try_into() .unwrap() } + + /// Return a Some(BoundedVec) or None if `v` is empty + /// # Example + /// ``` + /// use bounded_vec::BoundedVec; + /// use bounded_vec::OptBoundedVecToVec; + /// + /// let opt_bv_none = BoundedVec::::opt_empty_vec(vec![]).unwrap(); + /// assert!(opt_bv_none.is_none()); + /// assert_eq!(opt_bv_none.to_vec(), vec![]); + /// let opt_bv_some = BoundedVec::::opt_empty_vec(vec![0u8, 2]).unwrap(); + /// assert!(opt_bv_some.is_some()); + /// assert_eq!(opt_bv_some.to_vec(), vec![0u8, 2]); + /// ``` + pub fn opt_empty_vec(v: Vec) -> Result>, BoundedVecOutOfBounds> { + if v.is_empty() { + Ok(None) + } else { + Ok(Some(BoundedVec::from_vec(v)?)) + } + } +} + +impl BoundedVec { + /// Returns the first element of Vec + /// + /// # Example + /// ``` + /// use bounded_vec::BoundedVec; + /// use std::convert::TryInto; + /// + /// let data: BoundedVec<_, 0, 8> = vec![1u8, 2].try_into().unwrap(); + /// assert_eq!(data.try_first(), Some(&1)); + /// ``` + pub fn try_first(&self) -> Option<&T> { + self.inner.first() + } + + /// Returns the last element of Vec + /// + /// # Example + /// ``` + /// use bounded_vec::BoundedVec; + /// use std::convert::TryInto; + /// + /// let data: BoundedVec<_, 0, 8> = vec![1u8, 2].try_into().unwrap(); + /// assert_eq!(data.try_last(), Some(&2)); + /// ``` + pub fn try_last(&self) -> Option<&T> { + self.inner.last() + } + + /// Returns the last and all the rest of the elements + pub fn try_split_last(&self) -> Option<(&T, &[T])> { + self.inner.split_last() + } } /// A non-empty Vec with no effective upper-bound on its length @@ -359,7 +421,7 @@ impl<'a, T, const L: usize, const U: usize> IntoIterator for &'a BoundedVec; fn into_iter(self) -> Self::IntoIter { - (&self.inner).iter() + self.inner.iter() } } @@ -368,7 +430,7 @@ impl<'a, T, const L: usize, const U: usize> IntoIterator for &'a mut BoundedVec< type IntoIter = core::slice::IterMut<'a, T>; fn into_iter(self) -> Self::IntoIter { - (&mut self.inner).iter_mut() + self.inner.iter_mut() } } @@ -475,13 +537,29 @@ mod tests { #[test] fn first() { let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap(); - assert_eq!(data.first(), Some(&1u8)); + assert_eq!(data.first(), &1u8); } #[test] fn last() { let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap(); - assert_eq!(data.last(), Some(&2u8)); + assert_eq!(data.last(), &2u8); + } + + #[test] + fn try_first() { + let data: BoundedVec<_, 0, 8> = vec![1u8, 2].try_into().unwrap(); + assert_eq!(data.try_first(), Some(&1u8)); + let data: BoundedVec = vec![].try_into().unwrap(); + assert_eq!(data.try_first(), None); + } + + #[test] + fn try_last() { + let data: BoundedVec<_, 0, 8> = vec![1u8, 2].try_into().unwrap(); + assert_eq!(data.try_last(), Some(&2u8)); + let data: BoundedVec = vec![].try_into().unwrap(); + assert_eq!(data.try_last(), None); } #[test] @@ -536,9 +614,19 @@ mod tests { #[test] fn split_last() { let data: BoundedVec<_, 2, 8> = vec![1u8, 2].try_into().unwrap(); - assert_eq!(data.split_last(), Some((&2u8, [1u8].as_ref()))); + assert_eq!(data.split_last(), (&2u8, [1u8].as_ref())); let data1: BoundedVec<_, 1, 8> = vec![1u8].try_into().unwrap(); - assert_eq!(data1.split_last(), Some((&1u8, Vec::new().as_ref()))); + assert_eq!(data1.split_last(), (&1u8, Vec::new().as_ref())); + } + + #[test] + fn try_split_last() { + let data: BoundedVec<_, 0, 8> = vec![1u8, 2].try_into().unwrap(); + assert_eq!(data.try_split_last(), Some((&2u8, [1u8].as_ref()))); + let data1: BoundedVec<_, 0, 8> = vec![1u8].try_into().unwrap(); + assert_eq!(data1.try_split_last(), Some((&1u8, Vec::new().as_ref()))); + let data2: BoundedVec = vec![].try_into().unwrap(); + assert_eq!(data2.try_split_last(), None); } #[test]