Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(corelib): core::iter::chain #7064

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
2 changes: 2 additions & 0 deletions corelib/src/array.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,7 @@ impl SpanIterator<T> of Iterator<SpanIter<T>> {
}

impl SpanIntoIterator<T> of crate::iter::IntoIterator<Span<T>> {
type Item = @T;
type IntoIter = SpanIter<T>;
fn into_iter(self: Span<T>) -> Self::IntoIter {
SpanIter { span: self }
Expand Down Expand Up @@ -839,6 +840,7 @@ impl ArrayIterator<T> of Iterator<ArrayIter<T>> {
}

impl ArrayIntoIterator<T> of crate::iter::IntoIterator<Array<T>> {
type Item = T;
type IntoIter = ArrayIter<T>;
fn into_iter(self: Array<T>) -> Self::IntoIter {
ArrayIter { array: self }
Expand Down
1 change: 1 addition & 0 deletions corelib/src/byte_array.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,7 @@ impl ByteArrayIterator of crate::iter::Iterator<ByteArrayIter> {
}

impl ByteArrayIntoIterator of crate::iter::IntoIterator<ByteArray> {
type Item = u8;
type IntoIter = ByteArrayIter;
#[inline]
fn into_iter(self: ByteArray) -> Self::IntoIter {
Expand Down
2 changes: 2 additions & 0 deletions corelib/src/iter.cairo
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
mod adapters;
mod traits;
pub use adapters::chain;
pub use traits::{IntoIterator, Iterator};
2 changes: 2 additions & 0 deletions corelib/src/iter/adapters.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mod chain_adapter;
pub use chain_adapter::chain;
90 changes: 90 additions & 0 deletions corelib/src/iter/adapters/chain_adapter.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/// An iterator that links two iterators together, in a chain.
///
/// This `struct` is created by [`chain`]. See the
/// documentation for more.
#[derive(Drop)]
pub struct Chain<A, B> {
// These are "fused" with `Option` so we don't need separate state to track which part is
// already exhausted, and we may also get niche layout for `None`.
//
// Only the "first" iterator is actually set `None` when exhausted.
a: Option<A>,
b: Option<B>,
}

#[inline]
pub fn chained_iterator<A, B>(a: A, b: B) -> Chain<A, B> {
Chain { a: Option::Some(a), b: Option::Some(b) }
}

/// Converts the arguments to iterators and links them together, in a chain.
///
/// Arguments do not have to be of the same type as long as the underlying iterated
/// over items are.
///
/// # Examples
///
/// ```
/// use core::iter::chain;
///
/// let a: Array<u8> = array![7, 8, 9];
/// let b: Range<u8> = 0..5;
///
/// let mut iter = chain(a, b);
///
/// assert_eq!(iter.next(), Option::Some(7));
/// assert_eq!(iter.next(), Option::Some(8));
/// assert_eq!(iter.next(), Option::Some(9));
/// assert_eq!(iter.next(), Option::Some(0));
/// assert_eq!(iter.next(), Option::Some(1));
/// assert_eq!(iter.next(), Option::Some(2));
/// assert_eq!(iter.next(), Option::Some(3));
/// assert_eq!(iter.next(), Option::Some(4));
/// assert_eq!(iter.next(), Option::None);
/// ```
pub fn chain<
A,
B,
impl IntoIterA: IntoIterator<A>,
impl IntoIterB: IntoIterator<B>[Item: IntoIterA::Item],
+Drop<A>,
+Drop<B>,
+Drop<IntoIterA::IntoIter>,
>(
a: A, b: B,
) -> Chain<IntoIterA::IntoIter, IntoIterB::IntoIter> {
chained_iterator(a.into_iter(), b.into_iter())
}

impl ChainIterator<
A,
B,
impl IterA: Iterator<A>,
+Iterator<B>[Item: IterA::Item],
+Drop<A>,
+Drop<B>,
+Drop<IterA::Item>,
> of Iterator<Chain<A, B>> {
type Item = IterA::Item;

fn next(ref self: Chain<A, B>) -> Option<Self::Item> {
// First iterate over first container values
if self.a.is_some() {
let mut first_container = self.a.unwrap();
let value = first_container.next();
if value.is_some() {
self.a = Option::Some(first_container);
return value;
} else {
self.a = Option::None;
}
}

// Then iterate over second container values
let mut second_container = self.b.unwrap();
let value = second_container.next();
self.b = Option::Some(second_container);

value
}
}
10 changes: 9 additions & 1 deletion corelib/src/iter/traits/collect.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
///
/// // and we'll implement `IntoIterator`
/// impl MyCollectionIntoIterator of IntoIterator<MyCollection> {
/// type Item = u32;
/// type IntoIter = core::array::ArrayIter<u32>;
/// fn into_iter(self: MyCollection) -> Self::IntoIter {
/// self.arr.into_iter()
Expand All @@ -68,6 +69,9 @@
/// };
/// ```
pub trait IntoIterator<T> {
/// The type of the elements being iterated over.
type Item;

/// The iterator type that will be created.
type IntoIter;
impl Iterator: Iterator<Self::IntoIter>;
Expand All @@ -91,7 +95,10 @@ pub trait IntoIterator<T> {
fn into_iter(self: T) -> Self::IntoIter;
}

impl IteratorIntoIterator<T, +Iterator<T>> of IntoIterator<T> {
/// Trying to convert an already existing iterator into an iterator
/// just returns the iterator itself
impl IteratorIntoIterator<T, impl IterT: Iterator<T>> of IntoIterator<T> {
type Item = IterT::Item;
type IntoIter = T;
fn into_iter(self: T) -> T {
self
Expand All @@ -101,6 +108,7 @@ impl IteratorIntoIterator<T, +Iterator<T>> of IntoIterator<T> {
impl SnapshotFixedSizeArrayIntoIterator<
T, const SIZE: usize, +Drop<T>, impl ToSpan: core::array::ToSpanTrait<[T; SIZE], T>,
> of IntoIterator<@[T; SIZE]> {
type Item = @T;
type IntoIter = crate::array::SpanIter<T>;
fn into_iter(self: @[T; SIZE]) -> Self::IntoIter {
ToSpan::span(self).into_iter()
Expand Down
2 changes: 2 additions & 0 deletions corelib/src/ops/range.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ impl RangeIntoIterator<
+PartialOrd<T>,
-SierraIntRangeSupport<T>,
> of IntoIterator<Range<T>> {
type Item = T;
type IntoIter = RangeIterator<T>;
fn into_iter(self: Range<T>) -> Self::IntoIter {
let start = self.start;
Expand Down Expand Up @@ -173,6 +174,7 @@ trait SierraIntRangeSupport<T>;
impl SierraRangeIntoIterator<
T, +Copy<T>, +Drop<T>, +SierraIntRangeSupport<T>,
> of IntoIterator<Range<T>> {
type Item = T;
type IntoIter = internal::IntRange<T>;
fn into_iter(self: Range<T>) -> Self::IntoIter {
match internal::int_range_try_new(self.start, self.end) {
Expand Down
1 change: 1 addition & 0 deletions corelib/src/option.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,7 @@ impl OptionIterator<T> of crate::iter::Iterator<OptionIter<T>> {
}

impl OptionIntoIterator<T> of crate::iter::IntoIterator<Option<T>> {
type Item = T;
type IntoIter = OptionIter<T>;
#[inline]
fn into_iter(self: Option<T>) -> Self::IntoIter {
Expand Down
1 change: 1 addition & 0 deletions corelib/src/result.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,7 @@ pub impl ResultTraitImpl<T, E> of ResultTrait<T, E> {
impl ResultIntoIterator<
T, E, +Destruct<T>, +Destruct<E>,
> of crate::iter::IntoIterator<Result<T, E>> {
type Item = T;
type IntoIter = crate::option::OptionIter<T>;

/// Returns a consuming iterator over the possibly contained value.
Expand Down
1 change: 1 addition & 0 deletions corelib/src/test.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod felt_test;
mod fmt_test;
mod hash_test;
mod integer_test;
mod iter_test;
mod keccak_test;
mod math_test;
mod nullable_test;
Expand Down
36 changes: 36 additions & 0 deletions corelib/src/test/iter_test.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use core::iter::chain;
use core::ops::{Range};

#[test]
fn test_iterator_chain() {
let a = array![1, 2, 3];
let b = array![4, 5, 6];

let mut iter = chain(a, b);

assert_eq!(iter.next(), Option::Some(1));
assert_eq!(iter.next(), Option::Some(2));
assert_eq!(iter.next(), Option::Some(3));
assert_eq!(iter.next(), Option::Some(4));
assert_eq!(iter.next(), Option::Some(5));
assert_eq!(iter.next(), Option::Some(6));
assert_eq!(iter.next(), Option::None);
}

#[test]
fn test_iterator_chain_different_types() {
let a: Array<u8> = array![7, 8, 9];
let b: Range<u8> = 0..5;

let mut iter = chain(a, b);

assert_eq!(iter.next(), Option::Some(7));
assert_eq!(iter.next(), Option::Some(8));
assert_eq!(iter.next(), Option::Some(9));
assert_eq!(iter.next(), Option::Some(0));
assert_eq!(iter.next(), Option::Some(1));
assert_eq!(iter.next(), Option::Some(2));
assert_eq!(iter.next(), Option::Some(3));
assert_eq!(iter.next(), Option::Some(4));
assert_eq!(iter.next(), Option::None);
}