Skip to content

Commit 8ba6317

Browse files
committed
refactor: implement AtLeastOne type as a specialized version of Bounded
1 parent ddcfdad commit 8ba6317

File tree

1 file changed

+37
-225
lines changed

1 file changed

+37
-225
lines changed

zebra-chain/src/serialization/constraint.rs

Lines changed: 37 additions & 225 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ use crate::serialization::SerializationError;
4747
/// let vec: Vec<u32> = v.into_vec();
4848
/// # assert_eq!(vec, vec![2, 3, 5, 7, 11]);
4949
/// ```
50-
///
50+
///
5151
/// `Bounded` also re-implements some slice methods with different return
5252
/// types, to avoid redundant unwraps:
5353
/// ```
@@ -64,16 +64,6 @@ use crate::serialization::SerializationError;
6464
/// assert_eq!(rest, &[8, 9]);
6565
/// ```
6666
///
67-
/// **Push behavior:**
68-
/// `push` returns an error if it would exceed `MAX` length:
69-
/// ```
70-
/// # use zebra_chain::serialization::{Bounded, SerializationError};
71-
/// #
72-
/// # let mut v: Bounded<u32, 1, 2> = vec![1].try_into().unwrap();
73-
/// assert!(v.push(2).is_ok());
74-
/// assert!(v.push(3).is_err());
75-
/// ```
76-
///
7767
/// Unlike `Vec`, `first()` and `split_first()` never panic because the
7868
/// collection always contains at least `MIN` elements.
7969
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
@@ -234,17 +224,6 @@ impl<T, const MIN: usize, const MAX: usize> Bounded<T, MIN, MAX> {
234224
&mut self.inner[0]
235225
}
236226

237-
/// Attempts to append an element to the back of the collection.
238-
///
239-
/// Returns an error if appending the element would exceed the maximum length `MAX`.
240-
pub fn push(&mut self, element: T) -> Result<(), SerializationError> {
241-
if self.inner.len() >= MAX {
242-
return Err(SerializationError::Parse("too many elements"));
243-
}
244-
self.inner.push(element);
245-
Ok(())
246-
}
247-
248227
/// Returns the first and all the rest of the elements of the vector.
249228
///
250229
/// Unlike `Vec` or slice, `Bounded` always has a first element.
@@ -253,188 +232,38 @@ impl<T, const MIN: usize, const MAX: usize> Bounded<T, MIN, MAX> {
253232
}
254233
}
255234

256-
/// A `Vec<T>` wrapper that ensures there is at least one `T` in the vector.
235+
/// A `Vec<T>` wrapper that guarantees there is **at least one element** in the vector.
236+
///
237+
/// This is a specialized version of `Bounded<T, 1, usize::MAX>`, and can be used exactly like `Bounded`.
238+
/// In addition to the functionality of `Bounded`, it provides four convenience methods: `push`, `append`, `extend`, and `from_one`.
257239
///
258-
/// You can initialize `AtLeastOne` using:
240+
/// You can initialize and use `AtLeastOne` just like a `Bounded<T, 1, usize::MAX>`:
259241
/// ```
260242
/// # use zebra_chain::serialization::{AtLeastOne, SerializationError};
261243
/// # use std::convert::{TryFrom, TryInto};
262244
/// #
245+
/// // Initialize with one element
263246
/// let v: AtLeastOne<u32> = vec![42].try_into()?;
264-
/// assert_eq!(v.as_slice(), [42]);
265-
///
266-
/// let v: AtLeastOne<u32> = vec![42].as_slice().try_into()?;
267-
/// assert_eq!(v.as_slice(), [42]);
268-
///
269-
/// let v: AtLeastOne<u32> = [42].try_into()?;
270-
/// assert_eq!(v.as_slice(), [42]);
271-
///
272-
/// let v = AtLeastOne::<u32>::try_from(&[42])?;
273-
/// assert_eq!(v.as_slice(), [42]);
274-
/// #
275-
/// # Ok::<(), SerializationError>(())
276-
/// ```
277-
///
278-
/// And access the inner vector via [deref coercion](https://doc.rust-lang.org/std/ops/trait.Deref.html#more-on-deref-coercion),
279-
/// an explicit conversion, or as a slice:
280-
/// ```
281-
/// # use zebra_chain::serialization::AtLeastOne;
282-
/// # use std::convert::TryInto;
283-
/// #
284-
/// # let v: AtLeastOne<u32> = vec![42].try_into().unwrap();
285-
/// #
286-
/// let first = v.iter().next().expect("AtLeastOne always has a first element");
287-
/// assert_eq!(*first, 42);
247+
/// assert_eq!(v.as_slice(), &[42]);
288248
///
289-
/// let s = v.as_slice();
290-
/// #
291-
/// # assert_eq!(s, [42]);
249+
/// // Add elements using `push`, `append`, and `extend`
250+
/// let mut v: AtLeastOne<u32> = vec![42].try_into()?;
251+
/// v.push(43);
252+
/// assert_eq!(v.as_slice(), &[42, 43]);
292253
///
293-
/// let mut m = v.into_vec();
294-
/// #
295-
/// # assert_eq!(m.as_slice(), [42]);
254+
/// let mut other = vec![44, 45];
255+
/// v.append(&mut other);
256+
/// assert_eq!(v.as_slice(), &[42, 43, 44, 45]);
296257
///
297-
/// ```
258+
/// v.extend(vec![46, 47]);
259+
/// assert_eq!(v.as_slice(), &[42, 43, 44, 45, 46, 47]);
298260
///
299-
/// `AtLeastOne` also re-implements some slice methods with different return
300-
/// types, to avoid redundant unwraps:
301-
/// ```
302-
/// # use zebra_chain::serialization::AtLeastOne;
303-
/// # use std::convert::TryInto;
304-
/// #
305-
/// # let v: AtLeastOne<u32> = vec![42].try_into().unwrap();
306-
/// #
307-
/// let first = v.first();
308-
/// assert_eq!(*first, 42);
309-
///
310-
/// let (first, rest) = v.split_first();
311-
/// assert_eq!(*first, 42);
312-
/// assert!(rest.is_empty());
261+
/// // Initialize using `from_one`:
262+
/// let v: AtLeastOne<u32> = AtLeastOne::from_one(42);
263+
/// assert_eq!(v.as_slice(), &[42]);
264+
/// # Ok::<(), SerializationError>(())
313265
/// ```
314-
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
315-
pub struct AtLeastOne<T> {
316-
/// The inner vector, which must have at least one element.
317-
///
318-
/// `inner` is private, so that it can't be modified in ways that break the
319-
/// type constraint.
320-
inner: Vec<T>,
321-
}
322-
323-
// CORRECTNESS
324-
//
325-
// All conversions to `AtLeastOne<T>` must go through `TryFrom<Vec<T>>`,
326-
// so that the type constraint is satisfied.
327-
328-
impl<T> TryFrom<Vec<T>> for AtLeastOne<T> {
329-
type Error = SerializationError;
330-
331-
fn try_from(vec: Vec<T>) -> Result<Self, Self::Error> {
332-
if vec.is_empty() {
333-
Err(SerializationError::Parse("expected at least one item"))
334-
} else {
335-
Ok(AtLeastOne { inner: vec })
336-
}
337-
}
338-
}
339-
340-
impl<T> TryFrom<&Vec<T>> for AtLeastOne<T>
341-
where
342-
T: Clone,
343-
{
344-
type Error = SerializationError;
345-
346-
fn try_from(vec: &Vec<T>) -> Result<Self, Self::Error> {
347-
if vec.is_empty() {
348-
Err(SerializationError::Parse("expected at least one item"))
349-
} else {
350-
Ok(AtLeastOne {
351-
inner: vec.to_vec(),
352-
})
353-
}
354-
}
355-
}
356-
357-
impl<T> TryFrom<&[T]> for AtLeastOne<T>
358-
where
359-
T: Clone,
360-
{
361-
type Error = SerializationError;
362-
363-
fn try_from(slice: &[T]) -> Result<Self, Self::Error> {
364-
slice.to_vec().try_into()
365-
}
366-
}
367-
368-
// TODO:
369-
// - reject [T; 0] at compile time and impl From instead?
370-
impl<T, const N: usize> TryFrom<[T; N]> for AtLeastOne<T>
371-
where
372-
T: Clone,
373-
{
374-
type Error = SerializationError;
375-
376-
fn try_from(slice: [T; N]) -> Result<Self, Self::Error> {
377-
slice.to_vec().try_into()
378-
}
379-
}
380-
381-
// TODO:
382-
// - reject [T; 0] at compile time and impl From instead?
383-
// - remove when std is updated so that `TryFrom<&U>` is always implemented when
384-
// `TryFrom<U>`
385-
impl<T, const N: usize> TryFrom<&[T; N]> for AtLeastOne<T>
386-
where
387-
T: Clone,
388-
{
389-
type Error = SerializationError;
390-
391-
fn try_from(slice: &[T; N]) -> Result<Self, Self::Error> {
392-
slice.to_vec().try_into()
393-
}
394-
}
395-
396-
// Deref and AsRef (but not DerefMut or AsMut, because that could break the constraint)
397-
398-
impl<T> Deref for AtLeastOne<T> {
399-
type Target = Vec<T>;
400-
401-
fn deref(&self) -> &Self::Target {
402-
&self.inner
403-
}
404-
}
405-
406-
impl<T> AsRef<[T]> for AtLeastOne<T> {
407-
fn as_ref(&self) -> &[T] {
408-
self.inner.as_ref()
409-
}
410-
}
411-
412-
// Extracting one or more items
413-
414-
impl<T> From<AtLeastOne<T>> for Vec<T> {
415-
fn from(vec1: AtLeastOne<T>) -> Self {
416-
vec1.inner
417-
}
418-
}
419-
420-
// `IntoIterator` for `T` and `&mut T`, because iterators can't remove items
421-
422-
impl<T> IntoIterator for AtLeastOne<T> {
423-
type Item = T;
424-
425-
type IntoIter = std::vec::IntoIter<T>;
426-
427-
fn into_iter(self) -> std::vec::IntoIter<T> {
428-
self.inner.into_iter()
429-
}
430-
}
431-
432-
impl<T> AtLeastOne<T> {
433-
/// Returns an iterator that allows modifying each value.
434-
pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> {
435-
self.inner.iter_mut()
436-
}
437-
}
266+
pub type AtLeastOne<T> = Bounded<T, 1, { usize::MAX }>;
438267

439268
impl<T> AtLeastOne<T> {
440269
/// Returns a new `AtLeastOne`, containing a single `item`.
@@ -446,48 +275,31 @@ impl<T> AtLeastOne<T> {
446275
AtLeastOne { inner: vec![item] }
447276
}
448277

449-
/// Returns a reference to the inner vector.
450-
pub fn as_vec(&self) -> &Vec<T> {
451-
&self.inner
452-
}
453-
454-
/// Converts `self` into a vector without clones or allocation.
455-
///
456-
/// The resulting vector can be converted back into `AtLeastOne` via `try_into`.
457-
pub fn into_vec(self) -> Vec<T> {
458-
self.inner
459-
}
460-
461-
/// Returns the first element.
278+
/// Appends an element to the back of the collection.
462279
///
463-
/// Unlike `Vec` or slice, `AtLeastOne` always has a first element.
464-
pub fn first(&self) -> &T {
465-
&self.inner[0]
280+
/// This does not violate the `AtLeastOne` constraint since the collection
281+
/// always has at least one element.
282+
pub fn push(&mut self, element: T) {
283+
self.inner.push(element);
466284
}
467285

468-
/// Returns a mutable reference to the first element.
286+
/// Appends the elements of another collection.
469287
///
470-
/// Unlike `Vec` or slice, `AtLeastOne` always has a first element.
471-
pub fn first_mut(&mut self) -> &mut T {
472-
&mut self.inner[0]
473-
}
474-
475-
/// Appends an element to the back of the collection.
476-
pub fn push(&mut self, element: T) {
477-
self.inner.push(element);
288+
/// This does not violate the `AtLeastOne` constraint since the collection
289+
/// always has at least one element.
290+
pub fn append(&mut self, other: &mut Vec<T>) {
291+
self.inner.append(other);
478292
}
479293

480-
/// Returns the first and all the rest of the elements of the vector.
294+
/// Extends the collection with the elements of an iterator.
481295
///
482-
/// Unlike `Vec` or slice, `AtLeastOne` always has a first element.
483-
pub fn split_first(&self) -> (&T, &[T]) {
484-
(&self.inner[0], &self.inner[1..])
296+
/// This does not violate the `AtLeastOne` constraint since the collection
297+
/// always has at least one element.
298+
pub fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
299+
self.inner.extend(iter);
485300
}
486301
}
487302

488-
// TODO: consider implementing `push`, `append`, and `Extend`,
489-
// because adding elements can't break the constraint.
490-
491303
/// Create an initialized [`AtLeastOne`] instance.
492304
///
493305
/// This macro is similar to the [`vec!`][`std::vec!`] macro, but doesn't support creating an empty

0 commit comments

Comments
 (0)