Skip to content

Commit

Permalink
Introduce an Extend1 trait.
Browse files Browse the repository at this point in the history
This change implements `Extend1` for standard collections, which
supports bridging between zero-or-more and one-or-more collection type
by extending a zero-or-more collection with one or more items from an
`IntoIterator1`. This closes a gap in the APIs: without this, mapping a
standard collection to a non-empty collection required copying and
allocation, fallible construction and error checking, or unsafe code.
  • Loading branch information
olson-sean-k committed Oct 31, 2024
1 parent de4a892 commit 98d107c
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 7 deletions.
20 changes: 19 additions & 1 deletion src/array_vec1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use {

use crate::array1::Array1;
use crate::iter1::{
self, ExtendUntil, Feed, FromIterator1, FromIteratorUntil, IntoIterator1, Iterator1,
self, Extend1, ExtendUntil, Feed, FromIterator1, FromIteratorUntil, IntoIterator1, Iterator1,
};
use crate::safety::{self, ArrayVecExt as _, OptionExt as _, SliceExt as _};
use crate::segment::range::{self, PositionalRange, Project, ProjectionExt as _};
Expand All @@ -32,6 +32,24 @@ segment::impl_target_forward_type_and_definition!(
ArrayVecSegment,
);

impl<T, I, const N: usize> Extend1<I> for ArrayVec<T, N>
where
I: IntoIterator1<Item = T>,
// This bound isn't necessary for memory safety here, because an `ArrayVec` with no capacity
// panics when any item is inserted, so `extend_non_empty` panics. However, this bound is
// logically appropriate and prevents the definition of a function that always panics and has a
// nonsense output type.
[T; N]: Array1,
{
fn extend_non_empty(mut self, items: I) -> ArrayVec1<T, N> {
self.extend(items);
// SAFETY: The bound `[T; N]: Array1` guarantees that capacity is non-zero, input iterator
// `items` is non-empty, and `extend` either pushes one or more items or panics, so
// `self` must be non-empty here.
unsafe { ArrayVec1::from_array_vec_unchecked(self) }
}
}

impl<T, const N: usize> OrSaturated<T> for ArrayVec<T, N>
where
[T; N]: Array1,
Expand Down
15 changes: 14 additions & 1 deletion src/btree_map1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use core::ops::RangeBounds;

use crate::array1::Array1;
use crate::cmp::UnsafeOrd;
use crate::iter1::{self, FromIterator1, IntoIterator1, Iterator1};
use crate::iter1::{self, Extend1, FromIterator1, IntoIterator1, Iterator1};
use crate::safety::{self, NonZeroExt as _, OptionExt as _};
use crate::segment::range::{self, Intersect, RelationalRange};
use crate::segment::{self, Ranged, Segment, Segmentation, SegmentedOver};
Expand All @@ -23,6 +23,19 @@ segment::impl_target_forward_type_and_definition!(
BTreeMapSegment,
);

impl<K, V, I> Extend1<I> for BTreeMap<K, V>
where
K: Ord,
I: IntoIterator1<Item = (K, V)>,
{
fn extend_non_empty(mut self, items: I) -> BTreeMap1<K, V> {
self.extend(items);
// SAFETY: The input iterator `items` is non-empty and `extend` either pushes one or more
// items or panics, so `self` must be non-empty here.
unsafe { BTreeMap1::from_btree_map_unchecked(self) }
}
}

impl<K, V> Ranged for BTreeMap<K, V>
where
K: Clone + Ord,
Expand Down
15 changes: 14 additions & 1 deletion src/btree_set1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use core::ops::{BitAnd, BitOr, BitXor, RangeBounds, Sub};

use crate::array1::Array1;
use crate::cmp::UnsafeOrd;
use crate::iter1::{self, FromIterator1, IntoIterator1, Iterator1};
use crate::iter1::{self, Extend1, FromIterator1, IntoIterator1, Iterator1};
use crate::safety::{self, NonZeroExt as _, OptionExt as _};
use crate::segment::range::{self, Intersect, RelationalRange};
use crate::segment::{self, Ranged, Segment, Segmentation, SegmentedOver};
Expand All @@ -23,6 +23,19 @@ segment::impl_target_forward_type_and_definition!(
BTreeSetSegment,
);

impl<T, I> Extend1<I> for BTreeSet<T>
where
T: Ord,
I: IntoIterator1<Item = T>,
{
fn extend_non_empty(mut self, items: I) -> BTreeSet1<T> {
self.extend(items);
// SAFETY: The input iterator `items` is non-empty and `extend` either pushes one or more
// items or panics, so `self` must be non-empty here.
unsafe { BTreeSet1::from_btree_set_unchecked(self) }
}
}

impl<T> Ranged for BTreeSet<T>
where
T: Clone + Ord,
Expand Down
9 changes: 9 additions & 0 deletions src/iter1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ use {
};

use crate::safety::OptionExt as _;
use crate::NonEmpty;
use crate::NonZeroExt as _;
#[cfg(any(feature = "alloc", feature = "arrayvec"))]
use crate::Vacancy;
Expand Down Expand Up @@ -245,6 +246,14 @@ pub trait IteratorExt: Iterator + Sized {

impl<I> IteratorExt for I where I: Iterator {}

pub trait Extend1<I>
where
I: IntoIterator1,
{
#[must_use]
fn extend_non_empty(self, items: I) -> NonEmpty<Self>;
}

pub trait ExtendUntil<I>
where
I: IntoIterator,
Expand Down
8 changes: 6 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,11 @@
//! let xs = iter1::head_and_tail(0i32, [1, 2]);
//! let xs: Vec1<_> = xs.into_iter().skip(3).or_non_empty([3]).collect1();
//!
//! let ys = Vec::new();
//! let ys = ys.extend_non_empty([0i32]);
//!
//! assert_eq!(xs.as_slice(), &[3]);
//! assert_eq!(ys.as_slice(), &[0]);
#![doc = "```"]
//!
//! Non-empty collections and views naturally support iteration, collection, etc. via
Expand Down Expand Up @@ -314,8 +318,8 @@ pub mod prelude {

pub use crate::array1::Array1;
pub use crate::iter1::{
ExtendUntil, FromIterator1, FromIteratorUntil, IntoIterator1, IteratorExt as _, QueryAnd,
ThenIterator1,
Extend1, ExtendUntil, FromIterator1, FromIteratorUntil, IntoIterator1, IteratorExt as _,
QueryAnd, ThenIterator1,
};
pub use crate::slice1::{slice1, Slice1};
#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
Expand Down
14 changes: 13 additions & 1 deletion src/vec1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use std::io::{self, IoSlice, Write};

use crate::array1::Array1;
use crate::boxed1::{BoxedSlice1, BoxedSlice1Ext as _};
use crate::iter1::{self, ExtendUntil, FromIterator1, IntoIterator1, Iterator1};
use crate::iter1::{self, Extend1, ExtendUntil, FromIterator1, IntoIterator1, Iterator1};
use crate::safety::{self, NonZeroExt as _, OptionExt as _, SliceExt as _};
use crate::segment::range::{
self, Intersect, IntersectionExt as _, PositionalRange, Project, ProjectionExt as _,
Expand All @@ -33,6 +33,18 @@ segment::impl_target_forward_type_and_definition!(
VecSegment,
);

impl<T, I> Extend1<I> for Vec<T>
where
I: IntoIterator1<Item = T>,
{
fn extend_non_empty(mut self, items: I) -> Vec1<T> {
self.extend(items);
// SAFETY: The input iterator `items` is non-empty and `extend` either pushes one or more
// items or panics, so `self` must be non-empty here.
unsafe { Vec1::from_vec_unchecked(self) }
}
}

impl<T, I> ExtendUntil<I> for Vec<T>
where
I: IntoIterator<Item = T>,
Expand Down
14 changes: 13 additions & 1 deletion src/vec_deque1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use core::ops::{Index, IndexMut, RangeBounds};
use std::io::{self, IoSlice, Write};

use crate::array1::Array1;
use crate::iter1::{self, ExtendUntil, FromIterator1, IntoIterator1, Iterator1};
use crate::iter1::{self, Extend1, ExtendUntil, FromIterator1, IntoIterator1, Iterator1};
use crate::safety::{self, NonZeroExt as _, OptionExt as _};
use crate::segment::range::{self, PositionalRange, Project, ProjectionExt as _};
use crate::segment::{self, Ranged, Segment, Segmentation, SegmentedOver};
Expand All @@ -27,6 +27,18 @@ segment::impl_target_forward_type_and_definition!(
VecDequeSegment,
);

impl<T, I> Extend1<I> for VecDeque<T>
where
I: IntoIterator1<Item = T>,
{
fn extend_non_empty(mut self, items: I) -> VecDeque1<T> {
self.extend(items);
// SAFETY: The input iterator `items` is non-empty and `extend` either pushes one or more
// items or panics, so `self` must be non-empty here.
unsafe { VecDeque1::from_vec_deque_unchecked(self) }
}
}

impl<T, I> ExtendUntil<I> for VecDeque<T>
where
I: IntoIterator<Item = T>,
Expand Down

0 comments on commit 98d107c

Please sign in to comment.