sain.iter API documentation

# sain.iter

Composable external iteration. See `Iterator` for more details.

View Source

       1# BSD 3-Clause License
       2#
       3# Copyright (c) 2022-Present, nxtlo
       4# All rights reserved.
       5#
       6# Redistribution and use in source and binary forms, with or without
       7# modification, are permitted provided that the following conditions are met:
       8#
       9# * Redistributions of source code must retain the above copyright notice, this
      10#   list of conditions and the following disclaimer.
      11#
      12# * Redistributions in binary form must reproduce the above copyright notice,
      13#   this list of conditions and the following disclaimer in the documentation
      14#   and/or other materials provided with the distribution.
      15#
      16# * Neither the name of the copyright holder nor the names of its
      17#   contributors may be used to endorse or promote products derived from
      18#   this software without specific prior written permission.
      19#
      20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
      21# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      22# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
      23# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
      24# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      25# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      26# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
      27# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
      28# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      30"""Composable external iteration. See `Iterator` for more details."""
      31
      32from __future__ import annotations
      33
      34__all__ = (
      35    # Core
      36    "Iter",
      37    "Iterator",
      38    "TrustedIter",
      39    # Adapters
      40    "Cloned",
      41    "Copied",
      42    "Take",
      43    "Filter",
      44    "Map",
      45    "Skip",
      46    "Enumerate",
      47    "TakeWhile",
      48    "DropWhile",
      49    "Chunks",
      50    "Empty",
      51    "Repeat",
      52    "Once",
      53    "ExactSizeIterator",
      54    # Functions
      55    "into_iter",
      56    "empty",
      57    "once",
      58    "repeat",
      59)
      60
      61import abc
      62import collections.abc as collections
      63import copy
      64import itertools
      65import typing
      66
      67from . import default as _default
      68from . import futures
      69from . import option as _option
      70from . import result as _result
      71from .macros import rustc_diagnostic_item
      72from .macros import unsafe
      73
      74Item = typing.TypeVar("Item")
      75"""The type of the item that is being yielded."""
      76
      77OtherItem = typing.TypeVar("OtherItem")
      78"""The type of the item that is being mapped into then yielded."""
      79
      80AnyIter = typing.TypeVar("AnyIter", bound="Iterator[typing.Any]")
      81
      82
      83if typing.TYPE_CHECKING:
      84    import _typeshed
      85
      86    from .collections.slice import Slice
      87    from .collections.vec import Vec
      88    from .option import Option
      89
      90    Collector = (
      91        collections.MutableSequence[Item]
      92        | set[Item]
      93        | collections.MutableMapping[int, Item]
      94    )
      95    _typeshed.ConvertibleToInt
      96    Sum: typing.TypeAlias = (
      97        "Iterator[str]"
      98        | "Iterator[typing.SupportsInt]"
      99        | "Iterator[typing.SupportsIndex]"
     100        | "Iterator[_typeshed.ReadableBuffer]"
     101        | "Iterator[_typeshed.SupportsTrunc]"
     102        | "Iterator[float]"
     103    )
     104
     105
     106def unreachable() -> typing.NoReturn:
     107    raise StopIteration(
     108        "No more items exist in this iterator. It has been exhausted."
     109    ) from None
     110
     111
     112def oob() -> typing.NoReturn:
     113    raise IndexError("index is out of bounds.")
     114
     115
     116def diagnostic(cls: type[AnyIter]) -> type[AnyIter]:
     117    def _repr(self: Iterator[typing.Any]) -> str:
     118        return f"{type(self).__name__}(source: Iter<{type(self._it).__name__}>)"  # pyright: ignore
     119
     120    cls.__repr__ = _repr
     121    return cls
     122
     123
     124@rustc_diagnostic_item("Iterator")
     125class Iterator(
     126    typing.Generic[Item],
     127    abc.ABC,
     128    _default.Default["Empty[Item]"],
     129):
     130    """An abstract interface for dealing with iterators.
     131
     132    This is exactly the same trait as `core::iter::Iterator` trait from Rust.
     133
     134    This is the main interface that any type can implement by basically inheriting from it.
     135    The method `__next__` is the only method that needs to be implemented, You get all the other methods for free.
     136
     137    If you want to use a ready iterator for general purposes, Use `Iter`. This interface is only for implementers
     138    and type hints.
     139
     140    Example
     141    -------
     142    ```py
     143    @dataclass
     144    class Counter(Iterator[int]):
     145        start: int = 0
     146        stop: int | None = None
     147
     148        # implement the required method.
     149        def __next__(self) -> int:
     150            result = self.start
     151            self.start += 1
     152
     153            if self.stop is not None and result >= self.stop:
     154                raise StopIteration
     155
     156            return result
     157
     158    counter = Counter(start=0, stop=10)
     159    for i in counter.map(lambda x: x * 2): # multiply each number
     160        ...
     161    ```
     162    """
     163
     164    __slots__ = ()
     165
     166    @abc.abstractmethod
     167    def __next__(self) -> Item:
     168        raise NotImplementedError
     169
     170    ###################
     171    # const functions #
     172    ###################
     173
     174    @staticmethod
     175    @typing.final
     176    def default() -> Empty[Item]:
     177        """Return the default iterator for this type. It returns an empty iterator.
     178
     179        Example
     180        -------
     181        ```py
     182        it: Iterator[int] = Iter.default()
     183        assert t.next().is_none()
     184        ```
     185        """
     186        return Empty()
     187
     188    @typing.overload
     189    def collect(self) -> collections.MutableSequence[Item]: ...
     190
     191    @typing.overload
     192    def collect(
     193        self, *, cast: collections.Callable[[Item], OtherItem]
     194    ) -> collections.MutableSequence[OtherItem]: ...
     195
     196    @typing.final
     197    def collect(
     198        self, *, cast: collections.Callable[[Item], OtherItem] | None = None
     199    ) -> collections.MutableSequence[Item] | collections.MutableSequence[OtherItem]:
     200        """Collects all items in the iterator into a sequence.
     201
     202        Example
     203        -------
     204        ```py
     205        iterator = Iter(range(3))
     206        iterator.collect()
     207        # (0, 1, 2, 3)
     208        iterator.collect(cast=str) # Map each element and collect it.
     209        # ('0', '1', '2', '3')
     210        ```
     211
     212        Parameters
     213        ----------
     214        cast: `T | None`
     215            An optional type to cast the items into.
     216            If not provided the items will be returned as it's original type.
     217        """
     218        if cast is not None:
     219            return list(cast(i) for i in self)
     220
     221        return list(_ for _ in self)
     222
     223    @typing.final
     224    def collect_into(self, collection: Collector[Item]) -> None:
     225        """Consume this iterator, extending all items in the iterator into a mutable `collection`.
     226
     227        Example
     228        -------
     229        ```py
     230        iterator = Iter([1, 1, 2, 3, 4, 2, 6])
     231        uniques = set()
     232        iterator.collect_into(uniques)
     233        # assert uniques == {1, 2, 3, 4, 6}
     234        ```
     235
     236        Parameters
     237        ----------
     238        collection: `MutableSequence[T]` | `set[T]`
     239            The collection to extend the items in this iterator with.
     240        """
     241        if isinstance(collection, collections.MutableSequence):
     242            collection.extend(_ for _ in self)
     243        elif isinstance(collection, collections.MutableSet):
     244            collection.update(_ for _ in self)
     245        else:
     246            for idx, item in enumerate(self):
     247                collection[idx] = item
     248
     249    @typing.final
     250    def to_vec(self) -> Vec[Item]:
     251        """Convert this iterator into `Vec[T]`.
     252
     253        Example
     254        -------
     255        ```py
     256        it = sain.iter.once(0)
     257        vc = it.to_vec()
     258
     259        assert to_vec == [0]
     260        ```
     261        """
     262        from .collections.vec import Vec
     263
     264        return Vec(_ for _ in self)
     265
     266    @typing.final
     267    def sink(self) -> None:
     268        """Consume all elements from this iterator, flushing it into the sink.
     269
     270        Example
     271        -------
     272        ```py
     273        it = Iter((1, 2, 3))
     274        it.sink()
     275        assert it.next().is_none()
     276        ```
     277        """
     278        for _ in self:
     279            pass
     280
     281    @typing.final
     282    def raw_parts(self) -> collections.Generator[Item, None, None]:
     283        """Decompose this iterator into a `Generator[Item]` that yields all of the remaining items.
     284
     285        This mainly used for objects that needs to satisfy its exact type.
     286
     287        ```py
     288        it = Iter("cba")
     289        sort = sorted(it.raw_parts())
     290
     291        assert it.count() == 0
     292        assert sort == ["a", "b", "c"]
     293        ```
     294        """
     295        for item in self:
     296            yield item
     297
     298    ##################
     299    # default impl's #
     300    ##################
     301
     302    def next(self) -> Option[Item]:
     303        """Advance the iterator, Returning the next item, `Some(None)` if all items yielded.
     304
     305        Example
     306        -------
     307        ```py
     308        iterator = Iter(["1", "2"])
     309        assert iterator.next() == Some("1")
     310        assert iterator.next() == Some("2")
     311        assert iterator.next().is_none()
     312        ```
     313        """
     314        try:
     315            return _option.Some(self.__next__())
     316        except StopIteration:
     317            # ! SAFETY: No more items in the iterator.
     318            return _option.NOTHING
     319
     320    def cloned(self) -> Cloned[Item]:
     321        """Creates an iterator which shallow copies its elements by reference.
     322
     323        If you need a copy of the actual iterator and not the elements.
     324        use `Iter.clone()`
     325
     326        .. note::
     327            This method calls [`copy.copy()`](https://docs.python.org/3/library/copy.html)
     328            on each item that is being yielded.
     329
     330        Example
     331        -------
     332        ```py
     333        @dataclass
     334        class User:
     335            users_ids: list[int] = []
     336
     337        # An iterator which elements points to the same user.
     338        user = User()
     339        it = Iter((user, user))
     340
     341        for u in it.cloned():
     342            u.user_ids.append(1)
     343
     344        # We iterated over the same user pointer twice and appended "1"
     345        # since `copy` returns a shallow copy of nested structures.
     346        assert len(user.user_ids) == 2
     347        ```
     348        """
     349        return Cloned(self)
     350
     351    def copied(self) -> Copied[Item]:
     352        """Creates an iterator which copies all of its elements by value.
     353
     354        If you only need a copy of the item reference, Use `.cloned()` instead.
     355
     356        .. note::
     357            This method simply calls [`copy.deepcopy()`](https://docs.python.org/3/library/copy.html)
     358            on each item that is being yielded.
     359
     360        Example
     361        -------
     362        ```py
     363        @dataclass
     364        class User:
     365            users_ids: list[int] = []
     366
     367        # An iterator which elements points to the same user.
     368        user = User()
     369        it = Iter((user, user))
     370
     371        for u in it.copied():
     372            # A new list is created for each item.
     373            u.user_ids.append(1)
     374
     375        # The actual list is untouched since we consumed a deep copy of it.
     376        assert len(user.user_ids) == 0
     377        ```
     378        """
     379        return Copied(self)
     380
     381    def map(self, fn: collections.Callable[[Item], OtherItem]) -> Map[Item, OtherItem]:
     382        """Maps each item in the iterator to another type.
     383
     384        Example
     385        -------
     386        ```py
     387        iterator = Iter(["1", "2", "3"]).map(int)
     388
     389        for item in iterator:
     390            assert isinstance(item, int)
     391        ```
     392
     393        Parameters
     394        ----------
     395        predicate: `Callable[[Item], OtherItem]`
     396            The function to map each item in the iterator to the other type.
     397        """
     398        return Map(self, fn)
     399
     400    def filter(self, predicate: collections.Callable[[Item], bool]) -> Filter[Item]:
     401        """Filters the iterator to only yield items that match the predicate.
     402
     403        Example
     404        -------
     405        ```py
     406        places = Iter(['London', 'Paris', 'Los Angeles'])
     407        for place in places.filter(lambda place: place.startswith('L')):
     408            print(place)
     409
     410        # London
     411        # Los Angeles
     412        ```
     413        """
     414        return Filter(self, predicate)
     415
     416    def take(self, count: int) -> Take[Item]:
     417        """Take the first number of items until the number of items
     418        are yielded or the end of the iterator is exhausted.
     419
     420        Example
     421        -------
     422        ```py
     423        iterator = Iter(['c', 'x', 'y'])
     424
     425        for x in iterator.take(2):
     426            assert x in ('c', 'x')
     427
     428        # <Iter(['c', 'x'])>
     429        ```
     430        """
     431        return Take(self, count)
     432
     433    def skip(self, count: int) -> Skip[Item]:
     434        """Skips the first number of items in the iterator.
     435
     436        Example
     437        -------
     438        ```py
     439        iterator = Iter((1, 2, 3, 4))
     440        for i in iterator.skip(2):
     441            print(i)
     442
     443        # 3
     444        # 4
     445        ```
     446        """
     447        return Skip(self, count)
     448
     449    def enumerate(self, *, start: int = 0) -> Enumerate[Item]:
     450        """Create a new iterator that yields a tuple of the index and item.
     451
     452        Example
     453        -------
     454        ```py
     455        iterator = Iter([1, 2, 3])
     456        for index, item in iterator.enumerate():
     457            print(index, item)
     458
     459        # 0 1
     460        # 1 2
     461        # 2 3
     462        ```
     463        """
     464        return Enumerate(self, start)
     465
     466    def take_while(self, f: collections.Callable[[Item], bool]) -> TakeWhile[Item]:
     467        """yields items from the iterator while predicate returns `True`.
     468
     469        The rest of the items are discarded as soon as the predicate returns `False`
     470
     471        Example
     472        -------
     473        ```py
     474        iterator = Iter(['a', 'ab', 'xd', 'ba'])
     475        for x in iterator.take_while(lambda x: 'a' in x):
     476            print(x)
     477
     478        # a
     479        # ab
     480        ```
     481
     482        Parameters
     483        ----------
     484        predicate: `collections.Callable[[Item], bool]`
     485            The function to predicate each item in the iterator.
     486        """
     487        return TakeWhile(self, f)
     488
     489    def drop_while(self, f: collections.Callable[[Item], bool]) -> DropWhile[Item]:
     490        """Yields items from the iterator while predicate returns `False`.
     491
     492        Example
     493        -------
     494        ```py
     495        iterator = Iter(['a', 'ab', 'xd', 'ba'])
     496        for x in iterator.drop_while(lambda x: 'a' in x):
     497            print(x)
     498
     499        # xd
     500        # ba
     501        ```
     502
     503        Parameters
     504        ----------
     505        predicate: `collections.Callable[[Item], bool]`
     506            The function to predicate each item in the iterator.
     507        """
     508        return DropWhile(self, f)
     509
     510    def chunks(self, chunk_size: int, /) -> Chunks[Item]:
     511        """Returns an iterator over `chunk_size` elements of the iterator at a time,
     512        starting at the beginning of the iterator.
     513
     514        Example
     515        -------
     516        ```py
     517        iter = Iter(['a', 'b', 'c', 'd', 'e'])
     518        chunks = iter.chunks()
     519        assert chunks.next().unwrap() == ['a', 'b']
     520        assert chunks.next().unwrap() == ['c', 'd']
     521        assert chunks.next().unwrap() == ['e']
     522        assert chunks.next().is_none()
     523        ```
     524        """
     525        return Chunks(self, chunk_size)
     526
     527    def all(self, predicate: collections.Callable[[Item], bool]) -> bool:
     528        """Return `True` if all items in the iterator match the predicate.
     529
     530        Example
     531        -------
     532        ```py
     533        iterator = Iter([1, 2, 3])
     534        while iterator.all(lambda item: isinstance(item, int)):
     535            print("Still all integers")
     536            continue
     537            # Still all integers
     538        ```
     539
     540        Parameters
     541        ----------
     542        predicate: `collections.Callable[[Item], bool]`
     543            The function to test each item in the iterator.
     544        """
     545        return all(predicate(item) for item in self)
     546
     547    def any(self, predicate: collections.Callable[[Item], bool]) -> bool:
     548        """`True` if any items in the iterator match the predicate.
     549
     550        Example
     551        -------
     552        ```py
     553        iterator = Iter([1, 2, 3])
     554        if iterator.any(lambda item: isinstance(item, int)):
     555            print("At least one item is an int.")
     556        # At least one item is an int.
     557        ```
     558
     559        Parameters
     560        ----------
     561        predicate: `collections.Callable[[Item], bool]`
     562            The function to test each item in the iterator.
     563        """
     564        return any(predicate(item) for item in self)
     565
     566    def zip(
     567        self, other: collections.Iterable[OtherItem]
     568    ) -> Iter[tuple[Item, OtherItem]]:
     569        """Zips the iterator with another iterable.
     570
     571        Example
     572        -------
     573        ```py
     574        iterator = Iter([1, 2, 3])
     575        for item, other_item in iterator.zip([4, 5, 6]):
     576            assert item == other_item
     577        <Iter([(1, 4), (2, 5), (3, 6)])>
     578        ```
     579
     580        Parameters
     581        ----------
     582        other: `Iter[OtherItem]`
     583            The iterable to zip with.
     584
     585        Returns
     586        -------
     587        `Iter[tuple[Item, OtherItem]]`
     588            The zipped iterator.
     589
     590        """
     591        return Iter(zip(self.raw_parts(), other))
     592
     593    def sort(
     594        self,
     595        *,
     596        key: collections.Callable[[Item], _typeshed.SupportsRichComparison],
     597        reverse: bool = False,
     598    ) -> Iter[Item]:
     599        """Sorts the iterator.
     600
     601        Example
     602        -------
     603        ```py
     604        iterator = Iter([3, 1, 6, 7])
     605        for item in iterator.sort(key=lambda item: item < 3):
     606            print(item)
     607        # 1
     608        # 3
     609        # 6
     610        # 7
     611        ```
     612
     613        Parameters
     614        ----------
     615        key: `collections.Callable[[Item], Any]`
     616            The function to sort by.
     617        reverse: `bool`
     618            Whether to reverse the sort.
     619        """
     620        return Iter(sorted(self.raw_parts(), key=key, reverse=reverse))
     621
     622    def reversed(self) -> Iter[Item]:
     623        """Returns a new iterator that yields the items in the iterator in reverse order.
     624
     625        This consumes this iterator into a sequence and return a new iterator containing all of the elements
     626        in reversed order.
     627
     628        Example
     629        -------
     630        ```py
     631        iterator = Iter([3, 1, 6, 7])
     632        for item in iterator.reversed():
     633            print(item)
     634        # 7
     635        # 6
     636        # 1
     637        # 3
     638        ```
     639        """
     640        # NOTE: In order to reverse the iterator we need to
     641        # first collect it into some collection.
     642        return Iter(reversed(list(_ for _ in self)))
     643
     644    def union(self, other: collections.Iterable[Item]) -> Iter[Item]:
     645        """Returns a new iterator that yields all items from both iterators.
     646
     647        Example
     648        -------
     649        ```py
     650        iterator = Iter([1, 2, 3])
     651        other = [4, 5, 6]
     652
     653        for item in iterator.union(other):
     654            print(item)
     655        # 1
     656        # 2
     657        # 3
     658        # 4
     659        # 5
     660        # 6
     661        ```
     662
     663        Parameters
     664        ----------
     665        other: `Iter[Item]`
     666            The iterable to union with.
     667        """
     668        return Iter(itertools.chain(self.raw_parts(), other))
     669
     670    def first(self) -> Option[Item]:
     671        """Returns the first item in the iterator.
     672
     673        Example
     674        -------
     675        ```py
     676        iterator = Iter([3, 1, 6, 7])
     677        iterator.first().is_some_and(lambda x: x == 3)
     678        ```
     679        """
     680        return self.take(1).next()
     681
     682    def last(self) -> Option[Item]:
     683        """Returns the last item in the iterator.
     684
     685        Example
     686        -------
     687        ```py
     688        iterator = Iter([3, 1, 6, 7])
     689        iterator.last().is_some_and(lambda x: x == 7)
     690        ```
     691        """
     692        return self.reversed().first()
     693
     694    def count(self) -> int:
     695        """Return the count of elements in memory this iterator has.
     696
     697        Example
     698        -------
     699        ```py
     700        it = Iter(range(3))
     701        assert it.count() == 3
     702        ```
     703        """
     704        count = 0
     705        for _ in self:
     706            count += 1
     707
     708        return count
     709
     710    def find(self, predicate: collections.Callable[[Item], bool]) -> Option[Item]:
     711        """Searches for an element of an iterator that satisfies a predicate.
     712
     713        If you want the position of the element, use `Iterator.position` instead.
     714
     715        `find()` takes a lambda that returns true or false. It applies this closure to each element of the iterator,
     716        and if any of them return true, then find() returns `Some(element)`. If they all return false, it returns None.
     717
     718        Example
     719        -------
     720        ```py
     721        it = Iter(range(10))
     722        item = it.find(lambda num: num > 5)
     723        print(item) # 6
     724        ```
     725        """
     726        for item in self:
     727            if predicate(item):
     728                return _option.Some(item)
     729
     730        # no more items
     731        return _option.NOTHING
     732
     733    def position(self, predicate: collections.Callable[[Item], bool]) -> Option[int]:
     734        """Searches for the position of an element in the iterator that satisfies a predicate.
     735
     736        If you want the object itself, use `Iterator.find` instead.
     737
     738        `position()` takes a lambda that returns true or false. It applies this closure to each element of the iterator,
     739        and if any of them return true, then position() returns `Some(position_of_element)`. If they all return false, it returns None.
     740
     741        Example
     742        -------
     743        ```py
     744        it = Iter(range(10))
     745        position = it.find(lambda num: num > 5)
     746        assert position.unwrap() == 6
     747        ```
     748        """
     749        for position, value in self.enumerate():
     750            if predicate(value):
     751                return _option.Some(position)
     752
     753        # no more items
     754        return _option.NOTHING
     755
     756    def fold(
     757        self, init: OtherItem, f: collections.Callable[[OtherItem, Item], OtherItem]
     758    ) -> OtherItem:
     759        """Folds every element into an accumulator by applying an operation, returning the final result.
     760
     761        fold() takes two arguments: an initial value, and a closure with two arguments: an ‘accumulator’, and an element.
     762        The closure returns the value that the accumulator should have for the next iteration.
     763
     764        The initial value is the value the accumulator will have on the first call.
     765
     766        After applying this closure to every element of the iterator, fold() returns the accumulator.
     767
     768        This operation is sometimes called ‘reduce’ or ‘inject’.
     769
     770        Example
     771        -------
     772        ```py
     773        a = Iter([1, 2, 3, 4])
     774        sum = a.fold(0, lambda acc, elem: acc + elem)
     775        assert sum == 10
     776        ```
     777        """
     778        accum = init
     779        while True:
     780            try:
     781                x = self.__next__()
     782                accum = f(accum, x)
     783            except StopIteration:
     784                break
     785
     786        return accum
     787
     788    def advance_by(self, n: int) -> _result.Result[None, int]:
     789        """Advances the iterator by `n` elements.
     790
     791        Returns `Result[None, int]`, where `Ok(None)` means the iterator
     792        advanced successfully, and `Err(int)` if `None` encountered, where `int`
     793        represents the remaining number of steps that could not be advanced because the iterator ran out.
     794
     795        Example
     796        -------
     797        ```py
     798        it = into_iter([1, 2, 3, 4])
     799        assert it.advance_by(2).is_ok()
     800        assert it.next() == Some(3)
     801        assert it.advance_by(0).is_ok()
     802        assert it.advance_by(100) == Err(99)
     803        ```
     804        """
     805        for i in range(n):
     806            try:
     807                self.__next__()
     808            except StopIteration:
     809                return _result.Err(n - i)
     810
     811        return _result.Ok(None)
     812
     813    def nth(self, n: int) -> Option[Item]:
     814        """Returns the `n`th element of the iterator
     815
     816        Just like normal indexing, the count `n` starts from zero, so `nth(0)` returns the first
     817        value.
     818
     819        Note all elements before `n` will be skipped / consumed.
     820
     821        Example
     822        -------
     823        ```py
     824        a = into_iter([1, 2, 3])
     825        assert a.iter().nth(1) == Some(2)
     826        ```
     827        """
     828        for _ in range(n):
     829            try:
     830                self.__next__()
     831            except StopIteration:
     832                return _option.NOTHING
     833
     834        return self.next()
     835
     836    def sum(self: Sum) -> int:
     837        """Sums an iterator of a possible type `T` that can be converted to an integer.
     838
     839        where `T` is a typeof (`int`, `float`, `str`, `ReadableBuffer`, `SupportsTrunc`, `SupportsIndex`).
     840
     841        Example
     842        -------
     843        ```py
     844        numbers: Iterator[str] = Iter(["1", "2", "3"])
     845        total = numbers.sum()
     846        assert total == 6
     847        ```
     848        """
     849        return sum(int(_) for _ in self)
     850
     851    def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None:
     852        """Calls `func` on each item in the iterator.
     853
     854        Example
     855        -------
     856        ```py
     857        iterator = Iter([1, 2, 3])
     858        iterator.for_each(lambda item: print(item))
     859        # 1
     860        # 2
     861        # 3
     862        ```
     863
     864        Parameters
     865        ----------
     866        func: `collections.Callable[[Item], typing.Any]`
     867            The function to call on each item in the iterator.
     868        """
     869        for item in self:
     870            func(item)
     871
     872    async def async_for_each(
     873        self,
     874        func: collections.Callable[
     875            [Item], collections.Coroutine[typing.Any, typing.Any, OtherItem]
     876        ],
     877    ) -> _result.Result[collections.Sequence[OtherItem], futures.JoinError]:
     878        """Calls the async function on each item in the iterator *concurrently*.
     879
     880        Concurrently meaning that the next item will not wait for other items
     881        to finish to execute, each item gets called in a separate task.
     882
     883        After all the tasks finish, a `Result[list[T], JoinError]` will be returned,
     884        which will need to be handled by the caller.
     885
     886        Example
     887        -------
     888        ```py
     889        async def create_user(username: str) -> None:
     890            await aiohttp.request("POST", f'.../{username}')
     891
     892        async def main():
     893            users = sain.into_iter(["Danny", "Flower"])
     894            match await users.async_for_each(lambda username: create_user(username)):
     895                case Ok(result):
     896                    # all good
     897                case Err(why):
     898                    print(f"couldn't gather all futures, err={why}")
     899        ```
     900
     901        Parameters
     902        ----------
     903        func: `collections.Callable[[Item], Coroutine[None, Any, Any]]`
     904            The async function to call on each item in the iterator.
     905        """
     906        return await futures.join(*(func(item) for item in self))
     907
     908    def __reversed__(self) -> Iter[Item]:
     909        return self.reversed()
     910
     911    def __repr__(self) -> str:
     912        return "<Iterator>"
     913
     914    def __copy__(self) -> Cloned[Item]:
     915        return self.cloned()
     916
     917    def __deepcopy__(
     918        self, memo: collections.MutableMapping[int, typing.Any], /
     919    ) -> Copied[Item]:
     920        return self.copied()
     921
     922    def __len__(self) -> int:
     923        return self.count()
     924
     925    def __iter__(self) -> Iterator[Item]:
     926        return self
     927
     928
     929class ExactSizeIterator(typing.Generic[Item], Iterator[Item], abc.ABC):
     930    """An iterator that knows its exact size.
     931
     932    The implementations of this interface indicates that the iterator knows exactly
     933    how many items it can yield.
     934
     935    however, this is not a requirement for the iterator to implement this trait, as its
     936    only used for iterators that can know their size.
     937
     938    Example
     939    -------
     940    ```py
     941    @dataclass
     942    class Letters(ExactSizeIterator[str]):
     943        letters: list[str]
     944
     945        def __next__(self) -> str:
     946            return self.letters.pop(0)
     947
     948        def __len__(self) -> int:
     949            return len(self.letters)
     950
     951    letters = Letters(['a', 'b', 'c'])
     952    assert letters.count() == 3
     953    assert letters.next() == Some('a')
     954    assert letters.count() == 2
     955    ```
     956    """
     957
     958    __slots__ = ()
     959
     960    @typing.final
     961    def count(self) -> int:
     962        return len(self)
     963
     964    @typing.final
     965    def len(self) -> int:
     966        """Returns the remaining number of items in the iterator.
     967
     968        This doesn't exhaust the iterator.
     969
     970        Example
     971        -------
     972        ```py
     973        it = once(0)
     974        assert it.len() == 1
     975        assert it.len() == 1
     976        it.next()
     977        assert it.len() == 0
     978        ```
     979        """
     980        return len(self)
     981
     982    @typing.final
     983    def is_empty(self) -> bool:
     984        """Return `True` if this iterator has no items left to yield.
     985
     986        Example
     987        -------
     988        ```py
     989        iterator = once(1)
     990        assert not iterator.is_empty()
     991        assert once.next() == Some(1)
     992        assert iterator.is_empty()
     993        ```
     994        """
     995        return len(self) == 0
     996
     997    @abc.abstractmethod
     998    def __len__(self) -> int: ...
     999
    1000
    1001@rustc_diagnostic_item("Iter")
    1002@typing.final
    1003@diagnostic
    1004class Iter(typing.Generic[Item], Iterator[Item]):
    1005    """a lazy iterator that has its items ready in-memory.
    1006
    1007    This is similar to Rust `std::slice::Iter<T>` item which iterables can build
    1008    from this via `.iter()` method.
    1009
    1010    Example
    1011    -------
    1012    ```py
    1013    iterator = Iter([1, 2, 3])
    1014
    1015    # Limit the results to 2.
    1016    for item in iterator.take(2):
    1017        print(item)
    1018    # 1
    1019    # 2
    1020
    1021    # Filter the results.
    1022    for item in iterator.filter(lambda item: item > 1):
    1023        print(item)
    1024    # 2
    1025    # 3
    1026    # 3
    1027
    1028    # Indexing is supported.
    1029    print(iterator[0])
    1030    # 1
    1031    ```
    1032
    1033    Parameters
    1034    ----------
    1035    items: `Iterable[Item]`
    1036        The items to iterate over. This can be anything that implements `__iter__` and `__next__`.
    1037    """
    1038
    1039    __slots__ = ("_it",)
    1040
    1041    def __init__(self, iterable: collections.Iterable[Item]) -> None:
    1042        self._it = iter(iterable)
    1043
    1044    def clone(self) -> Iter[Item]:
    1045        """Return a copy of this iterator.
    1046
    1047        ```py
    1048        it = Iterator([1, 2, 3])
    1049
    1050        for i in it.clone():
    1051            ...
    1052
    1053        # The actual iterator hasn't been exhausted.
    1054        assert it.count() == 3
    1055        ```
    1056        """
    1057        return Iter(copy.copy(self._it))
    1058
    1059    def __next__(self) -> Item:
    1060        return next(self._it)
    1061
    1062    def __getitem__(self, index: int) -> Item:
    1063        return self.skip(index).first().unwrap_or_else(oob)
    1064
    1065    def __contains__(self, item: Item) -> bool:
    1066        return item in self._it
    1067
    1068
    1069@typing.final
    1070class TrustedIter(typing.Generic[Item], ExactSizeIterator[Item]):
    1071    """Similar to `Iter`, but it reports an accurate length using `ExactSizeIterator`.
    1072
    1073    iterable objects such as `Vec`, `Bytes`, `list` and other `Sized` may be created
    1074    using this iterator.
    1075
    1076    Example
    1077    -------
    1078    ```py
    1079    # we know the size of the iterator.
    1080    sized_buf: TrustedIter[int] = into_iter((1, 2, 3, 4))
    1081    # this is `Iter[int]` since we don't know when the generator will stop yielding.
    1082    unsized_buf: Iter[int] = into_iter((_ for _ in ([1, 2, 3, 4] if cond else [1, 2])))
    1083    ```
    1084
    1085    Parameters
    1086    ----------
    1087    items: `collections.Collection[Item]`
    1088        A sized collection of items to iterate over.
    1089    """
    1090
    1091    __slots__ = ("_it", "_len", "__alive")
    1092
    1093    def __init__(self, iterable: collections.Sequence[Item]) -> None:
    1094        self.__alive = iterable
    1095        self._len = len(iterable)
    1096        self._it = iter(iterable)
    1097
    1098    @property
    1099    def __slice_checked_get(self) -> collections.Sequence[Item] | None:
    1100        try:
    1101            return self.__alive
    1102        except AttributeError:
    1103            return None
    1104
    1105    def next(self) -> Option[Item]:
    1106        if self._len == 0:
    1107            # ! SAFETY: len == 0
    1108            return _option.NOTHING
    1109
    1110        return _option.Some(self.__next__())
    1111
    1112    @unsafe
    1113    def next_unchecked(self) -> Item:
    1114        """Returns the next item in the iterator without checking if it exists.
    1115
    1116        This is equivalent to calling `next()` on the iterator directly.
    1117
    1118        Example
    1119        -------
    1120        ```py
    1121        iterator = Iter([1])
    1122        assert iterator.next_unchecked() == 1
    1123        iterator.next_unchecked() # raises StopIteration
    1124        ```
    1125        """
    1126        return self.__next__()
    1127
    1128    @unsafe
    1129    def set_len(self, new_len: int) -> None:
    1130        """Sets the length of the iterator to `new_len`.
    1131
    1132        This is unsafe and should only be used if you know what you're doing.
    1133
    1134        Example
    1135        -------
    1136        ```py
    1137        iterator = Iter([1, 2, 3])
    1138        iterator.set_len(2)
    1139        assert iterator.len() == 2
    1140        ```
    1141        """
    1142        self._len = new_len
    1143
    1144    def as_slice(self) -> Slice[Item]:
    1145        """Returns an immutable slice of all elements that have not been yielded
    1146
    1147        Example
    1148        -------
    1149        ```py
    1150        iterator = into_iter([1, 2, 3])
    1151        iterator.as_slice() == [1, 2, 3]
    1152        iterator.next()
    1153        assert iterator.as_slice() == [2, 3]
    1154        ```
    1155        """
    1156        from .collections.slice import Slice
    1157
    1158        return Slice(self.__slice_checked_get or ())
    1159
    1160    def __repr__(self) -> str:
    1161        # __alive is dropped from `self`.
    1162        if (s := self.__slice_checked_get) is None:
    1163            return "TrustedIter(<empty>)"
    1164
    1165        return f"TrustedIter({s[-self._len :]})"
    1166
    1167    def __next__(self) -> Item:
    1168        try:
    1169            i = next(self._it)
    1170        except StopIteration:
    1171            # don't reference this anymore.
    1172            del self.__alive
    1173            raise
    1174
    1175        self._len -= 1
    1176        return i
    1177
    1178    def __getitem__(self, index: int) -> Item:
    1179        if self._len == 0:
    1180            raise IndexError("index out of bounds")
    1181
    1182        return self.skip(index).first().unwrap_or_else(oob)
    1183
    1184    def __contains__(self, item: Item) -> bool:
    1185        return item in self._it
    1186
    1187    def __len__(self) -> int:
    1188        return self._len
    1189
    1190
    1191@diagnostic
    1192class Cloned(typing.Generic[Item], Iterator[Item]):
    1193    """An iterator that copies the elements from an underlying iterator.
    1194
    1195    This iterator is created by the `Iterator.cloned` method.
    1196    """
    1197
    1198    __slots__ = ("_it",)
    1199
    1200    def __init__(self, it: Iterator[Item]) -> None:
    1201        self._it = it
    1202
    1203    def __next__(self) -> Item:
    1204        n = self._it.__next__()
    1205
    1206        # Avoid useless function call for a list.
    1207        if isinstance(n, list):
    1208            # SAFETY: We know this is a list.
    1209            return n[:]  # pyright: ignore
    1210
    1211        return copy.copy(n)
    1212
    1213
    1214@diagnostic
    1215class Copied(typing.Generic[Item], Iterator[Item]):
    1216    """An iterator that deeply-copies the elements from an underlying iterator.
    1217
    1218    This iterator is created by the `Iterator.copied` method.
    1219    """
    1220
    1221    __slots__ = ("_it",)
    1222
    1223    def __init__(self, it: Iterator[Item]) -> None:
    1224        self._it = it
    1225
    1226    def __next__(self) -> Item:
    1227        return copy.deepcopy(self._it.__next__())
    1228
    1229
    1230@diagnostic
    1231class Map(typing.Generic[Item, OtherItem], Iterator[OtherItem]):
    1232    """An iterator that maps the elements to a callable.
    1233
    1234    This iterator is created by the `Iterator.map` method.
    1235    """
    1236
    1237    __slots__ = ("_it", "_call")
    1238
    1239    def __init__(
    1240        self, it: Iterator[Item], call: collections.Callable[[Item], OtherItem]
    1241    ) -> None:
    1242        self._it = it
    1243        self._call = call
    1244
    1245    def __next__(self) -> OtherItem:
    1246        return self._call(self._it.__next__())
    1247
    1248
    1249@diagnostic
    1250class Filter(typing.Generic[Item], Iterator[Item]):
    1251    """An iterator that filters the elements to a `predicate`.
    1252
    1253    This iterator is created by the `Iterator.filter` method.
    1254    """
    1255
    1256    __slots__ = ("_it", "_call")
    1257
    1258    def __init__(
    1259        self, it: Iterator[Item], call: collections.Callable[[Item], bool]
    1260    ) -> None:
    1261        self._it = it
    1262        self._call = call
    1263
    1264    def __next__(self) -> Item:
    1265        for item in self._it:
    1266            if self._call(item):
    1267                return item
    1268
    1269        unreachable()
    1270
    1271
    1272@diagnostic
    1273class Take(typing.Generic[Item], Iterator[Item]):
    1274    """An iterator that yields the first `number` of elements and drops the rest.
    1275
    1276    This iterator is created by the `Iterator.take` method.
    1277    """
    1278
    1279    __slots__ = ("_it", "_taken", "_count")
    1280
    1281    def __init__(self, it: Iterator[Item], count: int) -> None:
    1282        if count <= 0:
    1283            raise ValueError("`count` must be non-zero")
    1284
    1285        self._it = it
    1286        self._taken = count
    1287        self._count = 0
    1288
    1289    def __next__(self) -> Item:
    1290        if self._count >= self._taken:
    1291            unreachable()
    1292
    1293        item = self._it.__next__()
    1294        self._count += 1
    1295        return item
    1296
    1297
    1298@diagnostic
    1299class Skip(typing.Generic[Item], Iterator[Item]):
    1300    """An iterator that skips the first `number` of elements and yields the rest.
    1301
    1302    This iterator is created by the `Iterator.skip` method.
    1303    """
    1304
    1305    __slots__ = ("_it", "_count", "_skipped")
    1306
    1307    def __init__(self, it: Iterator[Item], count: int) -> None:
    1308        if count <= 0:
    1309            raise ValueError("`count` must be non-zero")
    1310
    1311        self._it = it
    1312        self._count = count
    1313        self._skipped = 0
    1314
    1315    def __next__(self) -> Item:
    1316        while self._skipped < self._count:
    1317            self._skipped += 1
    1318            self._it.__next__()
    1319
    1320        return self._it.__next__()
    1321
    1322
    1323@diagnostic
    1324class Enumerate(typing.Generic[Item], Iterator[tuple[int, Item]]):
    1325    """An iterator that yields the current count and the element during iteration.
    1326
    1327    This iterator is created by the `Iterator.enumerate` method.
    1328    """
    1329
    1330    __slots__ = ("_it", "_count")
    1331
    1332    def __init__(self, it: Iterator[Item], start: int) -> None:
    1333        self._it = it
    1334        self._count = start
    1335
    1336    def __next__(self) -> tuple[int, Item]:
    1337        a = self._it.__next__()
    1338        i = self._count
    1339        self._count += 1
    1340        return i, a
    1341
    1342
    1343@diagnostic
    1344class TakeWhile(typing.Generic[Item], Iterator[Item]):
    1345    """An iterator that yields elements while `predicate` returns `True`.
    1346
    1347    This iterator is created by the `Iterator.take_while` method.
    1348    """
    1349
    1350    __slots__ = ("_it", "_predicate")
    1351
    1352    def __init__(
    1353        self, it: Iterator[Item], predicate: collections.Callable[[Item], bool]
    1354    ) -> None:
    1355        self._it = it
    1356        self._predicate = predicate
    1357
    1358    def __next__(self) -> Item:
    1359        item = self._it.__next__()
    1360
    1361        if self._predicate(item):
    1362            return item
    1363
    1364        unreachable()
    1365
    1366
    1367@diagnostic
    1368class DropWhile(typing.Generic[Item], Iterator[Item]):
    1369    """An iterator that yields elements while `predicate` returns `False`.
    1370
    1371    This iterator is created by the `Iterator.drop_while` method.
    1372    """
    1373
    1374    __slots__ = ("_it", "_predicate", "_dropped")
    1375
    1376    def __init__(
    1377        self, it: Iterator[Item], predicate: collections.Callable[[Item], bool]
    1378    ) -> None:
    1379        self._it = it
    1380        self._predicate = predicate
    1381        self._dropped = False
    1382
    1383    def __next__(self) -> Item:
    1384        if not self._dropped:
    1385            while not self._predicate(item := self._it.__next__()):
    1386                pass
    1387
    1388            self._dropped = True
    1389            return item
    1390
    1391        unreachable()
    1392
    1393
    1394@diagnostic
    1395class Chunks(typing.Generic[Item], Iterator[collections.Sequence[Item]]):
    1396    """An iterator that yields elements in chunks.
    1397
    1398    This iterator is created by the `Iterator.chunks` method.
    1399    """
    1400
    1401    __slots__ = ("chunk_size", "_it")
    1402
    1403    def __init__(self, it: Iterator[Item], chunk_size: int) -> None:
    1404        self.chunk_size = chunk_size
    1405        self._it = it
    1406
    1407    def __next__(self) -> collections.Sequence[Item]:
    1408        chunk: list[Item] = []
    1409
    1410        for item in self._it:
    1411            chunk.append(item)
    1412
    1413            if len(chunk) == self.chunk_size:
    1414                break
    1415
    1416        if chunk:
    1417            return chunk
    1418
    1419        unreachable()
    1420
    1421
    1422@typing.final
    1423@diagnostic
    1424class Empty(typing.Generic[Item], ExactSizeIterator[Item]):
    1425    """An iterator that yields nothing.
    1426
    1427    This is the default iterator that is created by `Iterator.default()` or `empty()`
    1428    """
    1429
    1430    __slots__ = ()
    1431
    1432    def __init__(self) -> None:
    1433        pass
    1434
    1435    def next(self) -> Option[Item]:
    1436        # SAFETY: an empty iterator always returns None.
    1437        # also we avoid calling `nothing_unchecked()` here for fast returns.
    1438        return _option.NOTHING
    1439
    1440    def __len__(self) -> typing.Literal[0]:
    1441        return 0
    1442
    1443    def any(
    1444        self, predicate: collections.Callable[[Item], bool]
    1445    ) -> typing.Literal[False]:
    1446        return False
    1447
    1448    def all(
    1449        self, predicate: collections.Callable[[Item], bool]
    1450    ) -> typing.Literal[False]:
    1451        return False
    1452
    1453    def __next__(self) -> Item:
    1454        unreachable()
    1455
    1456
    1457@typing.final
    1458@diagnostic
    1459class Repeat(typing.Generic[Item], ExactSizeIterator[Item]):
    1460    """An iterator that repeats a given value an exact number of times.
    1461
    1462    This iterator is created by calling `repeat()`.
    1463    """
    1464
    1465    __slots__ = ("_count", "_element")
    1466
    1467    def __init__(self, element: Item, count: int) -> None:
    1468        self._count = count
    1469        self._element = element
    1470
    1471    def __next__(self) -> Item:
    1472        if self._count > 0:
    1473            self._count -= 1
    1474            if self._count == 0:
    1475                # Return the origin element last
    1476                return self._element
    1477
    1478            return copy.copy(self._element)
    1479
    1480        unreachable()
    1481
    1482    def __len__(self) -> int:
    1483        return self._count
    1484
    1485
    1486@typing.final
    1487@diagnostic
    1488class Once(typing.Generic[Item], ExactSizeIterator[Item]):
    1489    """An iterator that yields exactly one item.
    1490
    1491    This iterator is created by calling `once()`.
    1492    """
    1493
    1494    __slots__ = ("_item",)
    1495
    1496    def __init__(self, item: Item) -> None:
    1497        self._item: Item | None = item
    1498
    1499    def __next__(self) -> Item:
    1500        if self._item is None:
    1501            unreachable()
    1502
    1503        i = self._item
    1504        self._item = None
    1505        return i
    1506
    1507    def __len__(self) -> int:
    1508        return 1 if self._item is not None else 0
    1509
    1510
    1511# a hack to trick the type-checker into thinking that this iterator yield `Item`.
    1512@rustc_diagnostic_item("empty")
    1513def empty() -> Empty[Item]:  # pyright: ignore
    1514    """Create an iterator that yields nothing.
    1515
    1516    Example
    1517    -------
    1518    ```py
    1519    nope: Iterator[int] = sain.iter.empty()
    1520    assert nope.next().is_none()
    1521    ```
    1522    """
    1523    return Empty()
    1524
    1525
    1526@rustc_diagnostic_item("repeat")
    1527def repeat(element: Item, count: int) -> Repeat[Item]:
    1528    """Returns an iterator that yields the exact same `element` number of `count` times.
    1529
    1530    The yielded elements is a copy of `element`, but the last element is guaranteed to be the same as the
    1531    original `element`.
    1532
    1533    Example
    1534    -------
    1535    ```py
    1536    nums = [1, 2, 3]
    1537    it = sain.iter.repeat(nums, 5)
    1538    for i in range(4):
    1539        cloned = it.next().unwrap()
    1540        assert cloned == [1, 2, 3]
    1541
    1542    # But the last item is the origin one...
    1543    last = it.next().unwrap()
    1544    last.append(4)
    1545    assert nums == [1, 2, 3, 4]
    1546    ```
    1547    """
    1548    return Repeat(element, count)
    1549
    1550
    1551@rustc_diagnostic_item("once")
    1552def once(item: Item) -> Once[Item]:
    1553    """Returns an iterator that yields exactly a single item.
    1554
    1555    Example
    1556    -------
    1557    ```py
    1558    iterator = sain.iter.once(1)
    1559    assert iterator.next() == Some(1)
    1560    assert iterator.next() == Some(None)
    1561    ```
    1562    """
    1563    return Once(item)
    1564
    1565
    1566@typing.overload
    1567def into_iter(
    1568    iterable: collections.Sequence[Item],
    1569) -> TrustedIter[Item]: ...
    1570
    1571
    1572@typing.overload
    1573def into_iter(
    1574    iterable: collections.Iterable[Item],
    1575    /,
    1576) -> Iter[Item]: ...
    1577
    1578
    1579@rustc_diagnostic_item("into_iter")
    1580def into_iter(
    1581    iterable: collections.Sequence[Item] | collections.Iterable[Item],
    1582) -> Iter[Item] | TrustedIter[Item] | TrustedIter[int]:
    1583    """Convert any iterable into `Iterator[Item]`.
    1584
    1585    if the size of the iterable is known, it will return `TrustedIter`,
    1586    otherwise it will return `Iter`.
    1587
    1588    Example
    1589    -------
    1590    ```py
    1591    sequence = [1,2,3]
    1592    for item in sain.into_iter(sequence).reversed():
    1593        print(item)
    1594    # 3
    1595    # 2
    1596    # 1
    1597    ```
    1598    """
    1599    if isinstance(iterable, collections.Sequence):
    1600        return TrustedIter(iterable)
    1601    return Iter(iterable)

@rustc_diagnostic_item('Iter')
@typing.final
@diagnostic
class Iter(typing.Generic[~Item], sain.iter.Iterator[~Item]): View Source

    1002@rustc_diagnostic_item("Iter")
    1003@typing.final
    1004@diagnostic
    1005class Iter(typing.Generic[Item], Iterator[Item]):
    1006    """a lazy iterator that has its items ready in-memory.
    1007
    1008    This is similar to Rust `std::slice::Iter<T>` item which iterables can build
    1009    from this via `.iter()` method.
    1010
    1011    Example
    1012    -------
    1013    ```py
    1014    iterator = Iter([1, 2, 3])
    1015
    1016    # Limit the results to 2.
    1017    for item in iterator.take(2):
    1018        print(item)
    1019    # 1
    1020    # 2
    1021
    1022    # Filter the results.
    1023    for item in iterator.filter(lambda item: item > 1):
    1024        print(item)
    1025    # 2
    1026    # 3
    1027    # 3
    1028
    1029    # Indexing is supported.
    1030    print(iterator[0])
    1031    # 1
    1032    ```
    1033
    1034    Parameters
    1035    ----------
    1036    items: `Iterable[Item]`
    1037        The items to iterate over. This can be anything that implements `__iter__` and `__next__`.
    1038    """
    1039
    1040    __slots__ = ("_it",)
    1041
    1042    def __init__(self, iterable: collections.Iterable[Item]) -> None:
    1043        self._it = iter(iterable)
    1044
    1045    def clone(self) -> Iter[Item]:
    1046        """Return a copy of this iterator.
    1047
    1048        ```py
    1049        it = Iterator([1, 2, 3])
    1050
    1051        for i in it.clone():
    1052            ...
    1053
    1054        # The actual iterator hasn't been exhausted.
    1055        assert it.count() == 3
    1056        ```
    1057        """
    1058        return Iter(copy.copy(self._it))
    1059
    1060    def __next__(self) -> Item:
    1061        return next(self._it)
    1062
    1063    def __getitem__(self, index: int) -> Item:
    1064        return self.skip(index).first().unwrap_or_else(oob)
    1065
    1066    def __contains__(self, item: Item) -> bool:
    1067        return item in self._it

a lazy iterator that has its items ready in-memory.

This is similar to Rust `std::slice::Iter<T>` item which iterables can build from this via `sain.iter` method.

###### Example

    iterator = Iter([1, 2, 3])

    # Limit the results to 2.
    for item in iterator.take(2):
        print(item)
    # 1
    # 2

    # Filter the results.
    for item in iterator.filter(lambda item: item > 1):
        print(item)
    # 2
    # 3
    # 3

    # Indexing is supported.
    print(iterator[0])
    # 1

###### Parameters

- items (`Iterable[Item]`): The items to iterate over. This can be anything that implements `__iter__` and `__next__`.
- # Implementations
- **This class implements Iter:

Iter(iterable: Iterable[~Item]) View Source

    1042    def __init__(self, iterable: collections.Iterable[Item]) -> None:
    1043        self._it = iter(iterable)

def clone(self) -> Iter[~Item]: View Source

    1045    def clone(self) -> Iter[Item]:
    1046        """Return a copy of this iterator.
    1047
    1048        ```py
    1049        it = Iterator([1, 2, 3])
    1050
    1051        for i in it.clone():
    1052            ...
    1053
    1054        # The actual iterator hasn't been exhausted.
    1055        assert it.count() == 3
    1056        ```
    1057        """
    1058        return Iter(copy.copy(self._it))

Return a copy of this iterator.

    it = Iterator([1, 2, 3])

    for i in it.clone():
        ...

    # The actual iterator hasn't been exhausted.
    assert it.count() == 3

##### Inherited Members

Iterator
  default
  collect
  collect_into
  to_vec
  sink
  raw_parts
  next
  cloned
  copied
  map
  filter
  take
  skip
  enumerate
  take_while
  drop_while
  chunks
  all
  any
  zip
  sort
  reversed
  union
  first
  last
  count
  find
  position
  fold
  advance_by
  nth
  sum
  for_each
  async_for_each

@rustc_diagnostic_item('Iterator')
class Iterator(typing.Generic[~Item], abc.ABC, sain.default.Default[ForwardRef('Empty[Item]')]): View Source

    125@rustc_diagnostic_item("Iterator")
    126class Iterator(
    127    typing.Generic[Item],
    128    abc.ABC,
    129    _default.Default["Empty[Item]"],
    130):
    131    """An abstract interface for dealing with iterators.
    132
    133    This is exactly the same trait as `core::iter::Iterator` trait from Rust.
    134
    135    This is the main interface that any type can implement by basically inheriting from it.
    136    The method `__next__` is the only method that needs to be implemented, You get all the other methods for free.
    137
    138    If you want to use a ready iterator for general purposes, Use `Iter`. This interface is only for implementers
    139    and type hints.
    140
    141    Example
    142    -------
    143    ```py
    144    @dataclass
    145    class Counter(Iterator[int]):
    146        start: int = 0
    147        stop: int | None = None
    148
    149        # implement the required method.
    150        def __next__(self) -> int:
    151            result = self.start
    152            self.start += 1
    153
    154            if self.stop is not None and result >= self.stop:
    155                raise StopIteration
    156
    157            return result
    158
    159    counter = Counter(start=0, stop=10)
    160    for i in counter.map(lambda x: x * 2): # multiply each number
    161        ...
    162    ```
    163    """
    164
    165    __slots__ = ()
    166
    167    @abc.abstractmethod
    168    def __next__(self) -> Item:
    169        raise NotImplementedError
    170
    171    ###################
    172    # const functions #
    173    ###################
    174
    175    @staticmethod
    176    @typing.final
    177    def default() -> Empty[Item]:
    178        """Return the default iterator for this type. It returns an empty iterator.
    179
    180        Example
    181        -------
    182        ```py
    183        it: Iterator[int] = Iter.default()
    184        assert t.next().is_none()
    185        ```
    186        """
    187        return Empty()
    188
    189    @typing.overload
    190    def collect(self) -> collections.MutableSequence[Item]: ...
    191
    192    @typing.overload
    193    def collect(
    194        self, *, cast: collections.Callable[[Item], OtherItem]
    195    ) -> collections.MutableSequence[OtherItem]: ...
    196
    197    @typing.final
    198    def collect(
    199        self, *, cast: collections.Callable[[Item], OtherItem] | None = None
    200    ) -> collections.MutableSequence[Item] | collections.MutableSequence[OtherItem]:
    201        """Collects all items in the iterator into a sequence.
    202
    203        Example
    204        -------
    205        ```py
    206        iterator = Iter(range(3))
    207        iterator.collect()
    208        # (0, 1, 2, 3)
    209        iterator.collect(cast=str) # Map each element and collect it.
    210        # ('0', '1', '2', '3')
    211        ```
    212
    213        Parameters
    214        ----------
    215        cast: `T | None`
    216            An optional type to cast the items into.
    217            If not provided the items will be returned as it's original type.
    218        """
    219        if cast is not None:
    220            return list(cast(i) for i in self)
    221
    222        return list(_ for _ in self)
    223
    224    @typing.final
    225    def collect_into(self, collection: Collector[Item]) -> None:
    226        """Consume this iterator, extending all items in the iterator into a mutable `collection`.
    227
    228        Example
    229        -------
    230        ```py
    231        iterator = Iter([1, 1, 2, 3, 4, 2, 6])
    232        uniques = set()
    233        iterator.collect_into(uniques)
    234        # assert uniques == {1, 2, 3, 4, 6}
    235        ```
    236
    237        Parameters
    238        ----------
    239        collection: `MutableSequence[T]` | `set[T]`
    240            The collection to extend the items in this iterator with.
    241        """
    242        if isinstance(collection, collections.MutableSequence):
    243            collection.extend(_ for _ in self)
    244        elif isinstance(collection, collections.MutableSet):
    245            collection.update(_ for _ in self)
    246        else:
    247            for idx, item in enumerate(self):
    248                collection[idx] = item
    249
    250    @typing.final
    251    def to_vec(self) -> Vec[Item]:
    252        """Convert this iterator into `Vec[T]`.
    253
    254        Example
    255        -------
    256        ```py
    257        it = sain.iter.once(0)
    258        vc = it.to_vec()
    259
    260        assert to_vec == [0]
    261        ```
    262        """
    263        from .collections.vec import Vec
    264
    265        return Vec(_ for _ in self)
    266
    267    @typing.final
    268    def sink(self) -> None:
    269        """Consume all elements from this iterator, flushing it into the sink.
    270
    271        Example
    272        -------
    273        ```py
    274        it = Iter((1, 2, 3))
    275        it.sink()
    276        assert it.next().is_none()
    277        ```
    278        """
    279        for _ in self:
    280            pass
    281
    282    @typing.final
    283    def raw_parts(self) -> collections.Generator[Item, None, None]:
    284        """Decompose this iterator into a `Generator[Item]` that yields all of the remaining items.
    285
    286        This mainly used for objects that needs to satisfy its exact type.
    287
    288        ```py
    289        it = Iter("cba")
    290        sort = sorted(it.raw_parts())
    291
    292        assert it.count() == 0
    293        assert sort == ["a", "b", "c"]
    294        ```
    295        """
    296        for item in self:
    297            yield item
    298
    299    ##################
    300    # default impl's #
    301    ##################
    302
    303    def next(self) -> Option[Item]:
    304        """Advance the iterator, Returning the next item, `Some(None)` if all items yielded.
    305
    306        Example
    307        -------
    308        ```py
    309        iterator = Iter(["1", "2"])
    310        assert iterator.next() == Some("1")
    311        assert iterator.next() == Some("2")
    312        assert iterator.next().is_none()
    313        ```
    314        """
    315        try:
    316            return _option.Some(self.__next__())
    317        except StopIteration:
    318            # ! SAFETY: No more items in the iterator.
    319            return _option.NOTHING
    320
    321    def cloned(self) -> Cloned[Item]:
    322        """Creates an iterator which shallow copies its elements by reference.
    323
    324        If you need a copy of the actual iterator and not the elements.
    325        use `Iter.clone()`
    326
    327        .. note::
    328            This method calls [`copy.copy()`](https://docs.python.org/3/library/copy.html)
    329            on each item that is being yielded.
    330
    331        Example
    332        -------
    333        ```py
    334        @dataclass
    335        class User:
    336            users_ids: list[int] = []
    337
    338        # An iterator which elements points to the same user.
    339        user = User()
    340        it = Iter((user, user))
    341
    342        for u in it.cloned():
    343            u.user_ids.append(1)
    344
    345        # We iterated over the same user pointer twice and appended "1"
    346        # since `copy` returns a shallow copy of nested structures.
    347        assert len(user.user_ids) == 2
    348        ```
    349        """
    350        return Cloned(self)
    351
    352    def copied(self) -> Copied[Item]:
    353        """Creates an iterator which copies all of its elements by value.
    354
    355        If you only need a copy of the item reference, Use `.cloned()` instead.
    356
    357        .. note::
    358            This method simply calls [`copy.deepcopy()`](https://docs.python.org/3/library/copy.html)
    359            on each item that is being yielded.
    360
    361        Example
    362        -------
    363        ```py
    364        @dataclass
    365        class User:
    366            users_ids: list[int] = []
    367
    368        # An iterator which elements points to the same user.
    369        user = User()
    370        it = Iter((user, user))
    371
    372        for u in it.copied():
    373            # A new list is created for each item.
    374            u.user_ids.append(1)
    375
    376        # The actual list is untouched since we consumed a deep copy of it.
    377        assert len(user.user_ids) == 0
    378        ```
    379        """
    380        return Copied(self)
    381
    382    def map(self, fn: collections.Callable[[Item], OtherItem]) -> Map[Item, OtherItem]:
    383        """Maps each item in the iterator to another type.
    384
    385        Example
    386        -------
    387        ```py
    388        iterator = Iter(["1", "2", "3"]).map(int)
    389
    390        for item in iterator:
    391            assert isinstance(item, int)
    392        ```
    393
    394        Parameters
    395        ----------
    396        predicate: `Callable[[Item], OtherItem]`
    397            The function to map each item in the iterator to the other type.
    398        """
    399        return Map(self, fn)
    400
    401    def filter(self, predicate: collections.Callable[[Item], bool]) -> Filter[Item]:
    402        """Filters the iterator to only yield items that match the predicate.
    403
    404        Example
    405        -------
    406        ```py
    407        places = Iter(['London', 'Paris', 'Los Angeles'])
    408        for place in places.filter(lambda place: place.startswith('L')):
    409            print(place)
    410
    411        # London
    412        # Los Angeles
    413        ```
    414        """
    415        return Filter(self, predicate)
    416
    417    def take(self, count: int) -> Take[Item]:
    418        """Take the first number of items until the number of items
    419        are yielded or the end of the iterator is exhausted.
    420
    421        Example
    422        -------
    423        ```py
    424        iterator = Iter(['c', 'x', 'y'])
    425
    426        for x in iterator.take(2):
    427            assert x in ('c', 'x')
    428
    429        # <Iter(['c', 'x'])>
    430        ```
    431        """
    432        return Take(self, count)
    433
    434    def skip(self, count: int) -> Skip[Item]:
    435        """Skips the first number of items in the iterator.
    436
    437        Example
    438        -------
    439        ```py
    440        iterator = Iter((1, 2, 3, 4))
    441        for i in iterator.skip(2):
    442            print(i)
    443
    444        # 3
    445        # 4
    446        ```
    447        """
    448        return Skip(self, count)
    449
    450    def enumerate(self, *, start: int = 0) -> Enumerate[Item]:
    451        """Create a new iterator that yields a tuple of the index and item.
    452
    453        Example
    454        -------
    455        ```py
    456        iterator = Iter([1, 2, 3])
    457        for index, item in iterator.enumerate():
    458            print(index, item)
    459
    460        # 0 1
    461        # 1 2
    462        # 2 3
    463        ```
    464        """
    465        return Enumerate(self, start)
    466
    467    def take_while(self, f: collections.Callable[[Item], bool]) -> TakeWhile[Item]:
    468        """yields items from the iterator while predicate returns `True`.
    469
    470        The rest of the items are discarded as soon as the predicate returns `False`
    471
    472        Example
    473        -------
    474        ```py
    475        iterator = Iter(['a', 'ab', 'xd', 'ba'])
    476        for x in iterator.take_while(lambda x: 'a' in x):
    477            print(x)
    478
    479        # a
    480        # ab
    481        ```
    482
    483        Parameters
    484        ----------
    485        predicate: `collections.Callable[[Item], bool]`
    486            The function to predicate each item in the iterator.
    487        """
    488        return TakeWhile(self, f)
    489
    490    def drop_while(self, f: collections.Callable[[Item], bool]) -> DropWhile[Item]:
    491        """Yields items from the iterator while predicate returns `False`.
    492
    493        Example
    494        -------
    495        ```py
    496        iterator = Iter(['a', 'ab', 'xd', 'ba'])
    497        for x in iterator.drop_while(lambda x: 'a' in x):
    498            print(x)
    499
    500        # xd
    501        # ba
    502        ```
    503
    504        Parameters
    505        ----------
    506        predicate: `collections.Callable[[Item], bool]`
    507            The function to predicate each item in the iterator.
    508        """
    509        return DropWhile(self, f)
    510
    511    def chunks(self, chunk_size: int, /) -> Chunks[Item]:
    512        """Returns an iterator over `chunk_size` elements of the iterator at a time,
    513        starting at the beginning of the iterator.
    514
    515        Example
    516        -------
    517        ```py
    518        iter = Iter(['a', 'b', 'c', 'd', 'e'])
    519        chunks = iter.chunks()
    520        assert chunks.next().unwrap() == ['a', 'b']
    521        assert chunks.next().unwrap() == ['c', 'd']
    522        assert chunks.next().unwrap() == ['e']
    523        assert chunks.next().is_none()
    524        ```
    525        """
    526        return Chunks(self, chunk_size)
    527
    528    def all(self, predicate: collections.Callable[[Item], bool]) -> bool:
    529        """Return `True` if all items in the iterator match the predicate.
    530
    531        Example
    532        -------
    533        ```py
    534        iterator = Iter([1, 2, 3])
    535        while iterator.all(lambda item: isinstance(item, int)):
    536            print("Still all integers")
    537            continue
    538            # Still all integers
    539        ```
    540
    541        Parameters
    542        ----------
    543        predicate: `collections.Callable[[Item], bool]`
    544            The function to test each item in the iterator.
    545        """
    546        return all(predicate(item) for item in self)
    547
    548    def any(self, predicate: collections.Callable[[Item], bool]) -> bool:
    549        """`True` if any items in the iterator match the predicate.
    550
    551        Example
    552        -------
    553        ```py
    554        iterator = Iter([1, 2, 3])
    555        if iterator.any(lambda item: isinstance(item, int)):
    556            print("At least one item is an int.")
    557        # At least one item is an int.
    558        ```
    559
    560        Parameters
    561        ----------
    562        predicate: `collections.Callable[[Item], bool]`
    563            The function to test each item in the iterator.
    564        """
    565        return any(predicate(item) for item in self)
    566
    567    def zip(
    568        self, other: collections.Iterable[OtherItem]
    569    ) -> Iter[tuple[Item, OtherItem]]:
    570        """Zips the iterator with another iterable.
    571
    572        Example
    573        -------
    574        ```py
    575        iterator = Iter([1, 2, 3])
    576        for item, other_item in iterator.zip([4, 5, 6]):
    577            assert item == other_item
    578        <Iter([(1, 4), (2, 5), (3, 6)])>
    579        ```
    580
    581        Parameters
    582        ----------
    583        other: `Iter[OtherItem]`
    584            The iterable to zip with.
    585
    586        Returns
    587        -------
    588        `Iter[tuple[Item, OtherItem]]`
    589            The zipped iterator.
    590
    591        """
    592        return Iter(zip(self.raw_parts(), other))
    593
    594    def sort(
    595        self,
    596        *,
    597        key: collections.Callable[[Item], _typeshed.SupportsRichComparison],
    598        reverse: bool = False,
    599    ) -> Iter[Item]:
    600        """Sorts the iterator.
    601
    602        Example
    603        -------
    604        ```py
    605        iterator = Iter([3, 1, 6, 7])
    606        for item in iterator.sort(key=lambda item: item < 3):
    607            print(item)
    608        # 1
    609        # 3
    610        # 6
    611        # 7
    612        ```
    613
    614        Parameters
    615        ----------
    616        key: `collections.Callable[[Item], Any]`
    617            The function to sort by.
    618        reverse: `bool`
    619            Whether to reverse the sort.
    620        """
    621        return Iter(sorted(self.raw_parts(), key=key, reverse=reverse))
    622
    623    def reversed(self) -> Iter[Item]:
    624        """Returns a new iterator that yields the items in the iterator in reverse order.
    625
    626        This consumes this iterator into a sequence and return a new iterator containing all of the elements
    627        in reversed order.
    628
    629        Example
    630        -------
    631        ```py
    632        iterator = Iter([3, 1, 6, 7])
    633        for item in iterator.reversed():
    634            print(item)
    635        # 7
    636        # 6
    637        # 1
    638        # 3
    639        ```
    640        """
    641        # NOTE: In order to reverse the iterator we need to
    642        # first collect it into some collection.
    643        return Iter(reversed(list(_ for _ in self)))
    644
    645    def union(self, other: collections.Iterable[Item]) -> Iter[Item]:
    646        """Returns a new iterator that yields all items from both iterators.
    647
    648        Example
    649        -------
    650        ```py
    651        iterator = Iter([1, 2, 3])
    652        other = [4, 5, 6]
    653
    654        for item in iterator.union(other):
    655            print(item)
    656        # 1
    657        # 2
    658        # 3
    659        # 4
    660        # 5
    661        # 6
    662        ```
    663
    664        Parameters
    665        ----------
    666        other: `Iter[Item]`
    667            The iterable to union with.
    668        """
    669        return Iter(itertools.chain(self.raw_parts(), other))
    670
    671    def first(self) -> Option[Item]:
    672        """Returns the first item in the iterator.
    673
    674        Example
    675        -------
    676        ```py
    677        iterator = Iter([3, 1, 6, 7])
    678        iterator.first().is_some_and(lambda x: x == 3)
    679        ```
    680        """
    681        return self.take(1).next()
    682
    683    def last(self) -> Option[Item]:
    684        """Returns the last item in the iterator.
    685
    686        Example
    687        -------
    688        ```py
    689        iterator = Iter([3, 1, 6, 7])
    690        iterator.last().is_some_and(lambda x: x == 7)
    691        ```
    692        """
    693        return self.reversed().first()
    694
    695    def count(self) -> int:
    696        """Return the count of elements in memory this iterator has.
    697
    698        Example
    699        -------
    700        ```py
    701        it = Iter(range(3))
    702        assert it.count() == 3
    703        ```
    704        """
    705        count = 0
    706        for _ in self:
    707            count += 1
    708
    709        return count
    710
    711    def find(self, predicate: collections.Callable[[Item], bool]) -> Option[Item]:
    712        """Searches for an element of an iterator that satisfies a predicate.
    713
    714        If you want the position of the element, use `Iterator.position` instead.
    715
    716        `find()` takes a lambda that returns true or false. It applies this closure to each element of the iterator,
    717        and if any of them return true, then find() returns `Some(element)`. If they all return false, it returns None.
    718
    719        Example
    720        -------
    721        ```py
    722        it = Iter(range(10))
    723        item = it.find(lambda num: num > 5)
    724        print(item) # 6
    725        ```
    726        """
    727        for item in self:
    728            if predicate(item):
    729                return _option.Some(item)
    730
    731        # no more items
    732        return _option.NOTHING
    733
    734    def position(self, predicate: collections.Callable[[Item], bool]) -> Option[int]:
    735        """Searches for the position of an element in the iterator that satisfies a predicate.
    736
    737        If you want the object itself, use `Iterator.find` instead.
    738
    739        `position()` takes a lambda that returns true or false. It applies this closure to each element of the iterator,
    740        and if any of them return true, then position() returns `Some(position_of_element)`. If they all return false, it returns None.
    741
    742        Example
    743        -------
    744        ```py
    745        it = Iter(range(10))
    746        position = it.find(lambda num: num > 5)
    747        assert position.unwrap() == 6
    748        ```
    749        """
    750        for position, value in self.enumerate():
    751            if predicate(value):
    752                return _option.Some(position)
    753
    754        # no more items
    755        return _option.NOTHING
    756
    757    def fold(
    758        self, init: OtherItem, f: collections.Callable[[OtherItem, Item], OtherItem]
    759    ) -> OtherItem:
    760        """Folds every element into an accumulator by applying an operation, returning the final result.
    761
    762        fold() takes two arguments: an initial value, and a closure with two arguments: an ‘accumulator’, and an element.
    763        The closure returns the value that the accumulator should have for the next iteration.
    764
    765        The initial value is the value the accumulator will have on the first call.
    766
    767        After applying this closure to every element of the iterator, fold() returns the accumulator.
    768
    769        This operation is sometimes called ‘reduce’ or ‘inject’.
    770
    771        Example
    772        -------
    773        ```py
    774        a = Iter([1, 2, 3, 4])
    775        sum = a.fold(0, lambda acc, elem: acc + elem)
    776        assert sum == 10
    777        ```
    778        """
    779        accum = init
    780        while True:
    781            try:
    782                x = self.__next__()
    783                accum = f(accum, x)
    784            except StopIteration:
    785                break
    786
    787        return accum
    788
    789    def advance_by(self, n: int) -> _result.Result[None, int]:
    790        """Advances the iterator by `n` elements.
    791
    792        Returns `Result[None, int]`, where `Ok(None)` means the iterator
    793        advanced successfully, and `Err(int)` if `None` encountered, where `int`
    794        represents the remaining number of steps that could not be advanced because the iterator ran out.
    795
    796        Example
    797        -------
    798        ```py
    799        it = into_iter([1, 2, 3, 4])
    800        assert it.advance_by(2).is_ok()
    801        assert it.next() == Some(3)
    802        assert it.advance_by(0).is_ok()
    803        assert it.advance_by(100) == Err(99)
    804        ```
    805        """
    806        for i in range(n):
    807            try:
    808                self.__next__()
    809            except StopIteration:
    810                return _result.Err(n - i)
    811
    812        return _result.Ok(None)
    813
    814    def nth(self, n: int) -> Option[Item]:
    815        """Returns the `n`th element of the iterator
    816
    817        Just like normal indexing, the count `n` starts from zero, so `nth(0)` returns the first
    818        value.
    819
    820        Note all elements before `n` will be skipped / consumed.
    821
    822        Example
    823        -------
    824        ```py
    825        a = into_iter([1, 2, 3])
    826        assert a.iter().nth(1) == Some(2)
    827        ```
    828        """
    829        for _ in range(n):
    830            try:
    831                self.__next__()
    832            except StopIteration:
    833                return _option.NOTHING
    834
    835        return self.next()
    836
    837    def sum(self: Sum) -> int:
    838        """Sums an iterator of a possible type `T` that can be converted to an integer.
    839
    840        where `T` is a typeof (`int`, `float`, `str`, `ReadableBuffer`, `SupportsTrunc`, `SupportsIndex`).
    841
    842        Example
    843        -------
    844        ```py
    845        numbers: Iterator[str] = Iter(["1", "2", "3"])
    846        total = numbers.sum()
    847        assert total == 6
    848        ```
    849        """
    850        return sum(int(_) for _ in self)
    851
    852    def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None:
    853        """Calls `func` on each item in the iterator.
    854
    855        Example
    856        -------
    857        ```py
    858        iterator = Iter([1, 2, 3])
    859        iterator.for_each(lambda item: print(item))
    860        # 1
    861        # 2
    862        # 3
    863        ```
    864
    865        Parameters
    866        ----------
    867        func: `collections.Callable[[Item], typing.Any]`
    868            The function to call on each item in the iterator.
    869        """
    870        for item in self:
    871            func(item)
    872
    873    async def async_for_each(
    874        self,
    875        func: collections.Callable[
    876            [Item], collections.Coroutine[typing.Any, typing.Any, OtherItem]
    877        ],
    878    ) -> _result.Result[collections.Sequence[OtherItem], futures.JoinError]:
    879        """Calls the async function on each item in the iterator *concurrently*.
    880
    881        Concurrently meaning that the next item will not wait for other items
    882        to finish to execute, each item gets called in a separate task.
    883
    884        After all the tasks finish, a `Result[list[T], JoinError]` will be returned,
    885        which will need to be handled by the caller.
    886
    887        Example
    888        -------
    889        ```py
    890        async def create_user(username: str) -> None:
    891            await aiohttp.request("POST", f'.../{username}')
    892
    893        async def main():
    894            users = sain.into_iter(["Danny", "Flower"])
    895            match await users.async_for_each(lambda username: create_user(username)):
    896                case Ok(result):
    897                    # all good
    898                case Err(why):
    899                    print(f"couldn't gather all futures, err={why}")
    900        ```
    901
    902        Parameters
    903        ----------
    904        func: `collections.Callable[[Item], Coroutine[None, Any, Any]]`
    905            The async function to call on each item in the iterator.
    906        """
    907        return await futures.join(*(func(item) for item in self))
    908
    909    def __reversed__(self) -> Iter[Item]:
    910        return self.reversed()
    911
    912    def __repr__(self) -> str:
    913        return "<Iterator>"
    914
    915    def __copy__(self) -> Cloned[Item]:
    916        return self.cloned()
    917
    918    def __deepcopy__(
    919        self, memo: collections.MutableMapping[int, typing.Any], /
    920    ) -> Copied[Item]:
    921        return self.copied()
    922
    923    def __len__(self) -> int:
    924        return self.count()
    925
    926    def __iter__(self) -> Iterator[Item]:
    927        return self

An abstract interface for dealing with iterators.

This is exactly the same trait as `core::iter::Iterator` trait from Rust.

This is the main interface that any type can implement by basically inheriting from it. The method `__next__` is the only method that needs to be implemented, You get all the other methods for free.

If you want to use a ready iterator for general purposes, Use `Iter`. This interface is only for implementers and type hints.

###### Example

    @dataclass
    class Counter(Iterator[int]):
        start: int = 0
        stop: int | None = None

        # implement the required method.
        def __next__(self) -> int:
            result = self.start
            self.start += 1

            if self.stop is not None and result >= self.stop:
                raise StopIteration

            return result

    counter = Counter(start=0, stop=10)
    for i in counter.map(lambda x: x * 2): # multiply each number
        ...

# Implementations

This class implements Iterator in Rust.

@staticmethod
@typing.final
def default() -> Empty[~Item]: View Source

    175    @staticmethod
    176    @typing.final
    177    def default() -> Empty[Item]:
    178        """Return the default iterator for this type. It returns an empty iterator.
    179
    180        Example
    181        -------
    182        ```py
    183        it: Iterator[int] = Iter.default()
    184        assert t.next().is_none()
    185        ```
    186        """
    187        return Empty()

Return the default iterator for this type. It returns an empty iterator.

###### Example

    it: Iterator[int] = Iter.default()
    assert t.next().is_none()

@typing.final
def collect( self, *, cast: Callable[[~Item], ~OtherItem] | None = None) -> MutableSequence[~Item] | MutableSequence[~OtherItem]: View Source

    197    @typing.final
    198    def collect(
    199        self, *, cast: collections.Callable[[Item], OtherItem] | None = None
    200    ) -> collections.MutableSequence[Item] | collections.MutableSequence[OtherItem]:
    201        """Collects all items in the iterator into a sequence.
    202
    203        Example
    204        -------
    205        ```py
    206        iterator = Iter(range(3))
    207        iterator.collect()
    208        # (0, 1, 2, 3)
    209        iterator.collect(cast=str) # Map each element and collect it.
    210        # ('0', '1', '2', '3')
    211        ```
    212
    213        Parameters
    214        ----------
    215        cast: `T | None`
    216            An optional type to cast the items into.
    217            If not provided the items will be returned as it's original type.
    218        """
    219        if cast is not None:
    220            return list(cast(i) for i in self)
    221
    222        return list(_ for _ in self)

Collects all items in the iterator into a sequence.

###### Example

    iterator = Iter(range(3))
    iterator.collect()
    # (0, 1, 2, 3)
    iterator.collect(cast=str) # Map each element and collect it.
    # ('0', '1', '2', '3')

###### Parameters

- cast (`T | None`): An optional type to cast the items into. If not provided the items will be returned as it's original type.

@typing.final
def collect_into(self, collection: 'Collector[Item]') -> None: View Source

    224    @typing.final
    225    def collect_into(self, collection: Collector[Item]) -> None:
    226        """Consume this iterator, extending all items in the iterator into a mutable `collection`.
    227
    228        Example
    229        -------
    230        ```py
    231        iterator = Iter([1, 1, 2, 3, 4, 2, 6])
    232        uniques = set()
    233        iterator.collect_into(uniques)
    234        # assert uniques == {1, 2, 3, 4, 6}
    235        ```
    236
    237        Parameters
    238        ----------
    239        collection: `MutableSequence[T]` | `set[T]`
    240            The collection to extend the items in this iterator with.
    241        """
    242        if isinstance(collection, collections.MutableSequence):
    243            collection.extend(_ for _ in self)
    244        elif isinstance(collection, collections.MutableSet):
    245            collection.update(_ for _ in self)
    246        else:
    247            for idx, item in enumerate(self):
    248                collection[idx] = item

Consume this iterator, extending all items in the iterator into a mutable `collection`.

###### Example

    iterator = Iter([1, 1, 2, 3, 4, 2, 6])
    uniques = set()
    iterator.collect_into(uniques)
    # assert uniques == {1, 2, 3, 4, 6}

###### Parameters

- collection (`MutableSequence[T]` | `set[T]`): The collection to extend the items in this iterator with.

@typing.final
def to_vec(self) -> 'Vec[Item]': View Source

    250    @typing.final
    251    def to_vec(self) -> Vec[Item]:
    252        """Convert this iterator into `Vec[T]`.
    253
    254        Example
    255        -------
    256        ```py
    257        it = sain.iter.once(0)
    258        vc = it.to_vec()
    259
    260        assert to_vec == [0]
    261        ```
    262        """
    263        from .collections.vec import Vec
    264
    265        return Vec(_ for _ in self)

Convert this iterator into `Vec[T]`.

###### Example

    it = sain.iter.once(0)
    vc = it.to_vec()

    assert to_vec == [0]

@typing.final
def sink(self) -> None: View Source

    267    @typing.final
    268    def sink(self) -> None:
    269        """Consume all elements from this iterator, flushing it into the sink.
    270
    271        Example
    272        -------
    273        ```py
    274        it = Iter((1, 2, 3))
    275        it.sink()
    276        assert it.next().is_none()
    277        ```
    278        """
    279        for _ in self:
    280            pass

Consume all elements from this iterator, flushing it into the sink.

###### Example

    it = Iter((1, 2, 3))
    it.sink()
    assert it.next().is_none()

@typing.final
def raw_parts(self) -> Generator[~Item, None, None]: View Source

    282    @typing.final
    283    def raw_parts(self) -> collections.Generator[Item, None, None]:
    284        """Decompose this iterator into a `Generator[Item]` that yields all of the remaining items.
    285
    286        This mainly used for objects that needs to satisfy its exact type.
    287
    288        ```py
    289        it = Iter("cba")
    290        sort = sorted(it.raw_parts())
    291
    292        assert it.count() == 0
    293        assert sort == ["a", "b", "c"]
    294        ```
    295        """
    296        for item in self:
    297            yield item

Decompose this iterator into a `Generator[Item]` that yields all of the remaining items.

This mainly used for objects that needs to satisfy its exact type.

    it = Iter("cba")
    sort = sorted(it.raw_parts())

    assert it.count() == 0
    assert sort == ["a", "b", "c"]

def next(self) -> 'Option[Item]': View Source

    303    def next(self) -> Option[Item]:
    304        """Advance the iterator, Returning the next item, `Some(None)` if all items yielded.
    305
    306        Example
    307        -------
    308        ```py
    309        iterator = Iter(["1", "2"])
    310        assert iterator.next() == Some("1")
    311        assert iterator.next() == Some("2")
    312        assert iterator.next().is_none()
    313        ```
    314        """
    315        try:
    316            return _option.Some(self.__next__())
    317        except StopIteration:
    318            # ! SAFETY: No more items in the iterator.
    319            return _option.NOTHING

Advance the iterator, Returning the next item, `Some(None)` if all items yielded.

###### Example

    iterator = Iter(["1", "2"])
    assert iterator.next() == Some("1")
    assert iterator.next() == Some("2")
    assert iterator.next().is_none()

def cloned(self) -> Cloned[~Item]: View Source

    321    def cloned(self) -> Cloned[Item]:
    322        """Creates an iterator which shallow copies its elements by reference.
    323
    324        If you need a copy of the actual iterator and not the elements.
    325        use `Iter.clone()`
    326
    327        .. note::
    328            This method calls [`copy.copy()`](https://docs.python.org/3/library/copy.html)
    329            on each item that is being yielded.
    330
    331        Example
    332        -------
    333        ```py
    334        @dataclass
    335        class User:
    336            users_ids: list[int] = []
    337
    338        # An iterator which elements points to the same user.
    339        user = User()
    340        it = Iter((user, user))
    341
    342        for u in it.cloned():
    343            u.user_ids.append(1)
    344
    345        # We iterated over the same user pointer twice and appended "1"
    346        # since `copy` returns a shallow copy of nested structures.
    347        assert len(user.user_ids) == 2
    348        ```
    349        """
    350        return Cloned(self)

Creates an iterator which shallow copies its elements by reference.

If you need a copy of the actual iterator and not the elements. use `Iter.clone()`

This method calls `copy.copy()` on each item that is being yielded.

###### Example

    @dataclass
    class User:
        users_ids: list[int] = []

    # An iterator which elements points to the same user.
    user = User()
    it = Iter((user, user))

    for u in it.cloned():
        u.user_ids.append(1)

    # We iterated over the same user pointer twice and appended "1"
    # since `copy` returns a shallow copy of nested structures.
    assert len(user.user_ids) == 2

def copied(self) -> Copied[~Item]: View Source

    352    def copied(self) -> Copied[Item]:
    353        """Creates an iterator which copies all of its elements by value.
    354
    355        If you only need a copy of the item reference, Use `.cloned()` instead.
    356
    357        .. note::
    358            This method simply calls [`copy.deepcopy()`](https://docs.python.org/3/library/copy.html)
    359            on each item that is being yielded.
    360
    361        Example
    362        -------
    363        ```py
    364        @dataclass
    365        class User:
    366            users_ids: list[int] = []
    367
    368        # An iterator which elements points to the same user.
    369        user = User()
    370        it = Iter((user, user))
    371
    372        for u in it.copied():
    373            # A new list is created for each item.
    374            u.user_ids.append(1)
    375
    376        # The actual list is untouched since we consumed a deep copy of it.
    377        assert len(user.user_ids) == 0
    378        ```
    379        """
    380        return Copied(self)

Creates an iterator which copies all of its elements by value.

If you only need a copy of the item reference, Use `.cloned()` instead.

This method simply calls `copy.deepcopy()` on each item that is being yielded.

###### Example

    @dataclass
    class User:
        users_ids: list[int] = []

    # An iterator which elements points to the same user.
    user = User()
    it = Iter((user, user))

    for u in it.copied():
        # A new list is created for each item.
        u.user_ids.append(1)

    # The actual list is untouched since we consumed a deep copy of it.
    assert len(user.user_ids) == 0

def map( self, fn: Callable[[~Item], ~OtherItem]) -> Map[~Item, ~OtherItem]: View Source

    382    def map(self, fn: collections.Callable[[Item], OtherItem]) -> Map[Item, OtherItem]:
    383        """Maps each item in the iterator to another type.
    384
    385        Example
    386        -------
    387        ```py
    388        iterator = Iter(["1", "2", "3"]).map(int)
    389
    390        for item in iterator:
    391            assert isinstance(item, int)
    392        ```
    393
    394        Parameters
    395        ----------
    396        predicate: `Callable[[Item], OtherItem]`
    397            The function to map each item in the iterator to the other type.
    398        """
    399        return Map(self, fn)

Maps each item in the iterator to another type.

###### Example

    iterator = Iter(["1", "2", "3"]).map(int)

    for item in iterator:
        assert isinstance(item, int)

###### Parameters

- predicate (`Callable[[Item], OtherItem]`): The function to map each item in the iterator to the other type.

def filter(self, predicate: Callable[[~Item], bool]) -> Filter[~Item]: View Source

    401    def filter(self, predicate: collections.Callable[[Item], bool]) -> Filter[Item]:
    402        """Filters the iterator to only yield items that match the predicate.
    403
    404        Example
    405        -------
    406        ```py
    407        places = Iter(['London', 'Paris', 'Los Angeles'])
    408        for place in places.filter(lambda place: place.startswith('L')):
    409            print(place)
    410
    411        # London
    412        # Los Angeles
    413        ```
    414        """
    415        return Filter(self, predicate)

Filters the iterator to only yield items that match the predicate.

###### Example

    places = Iter(['London', 'Paris', 'Los Angeles'])
    for place in places.filter(lambda place: place.startswith('L')):
        print(place)

    # London
    # Los Angeles

def take(self, count: int) -> Take[~Item]: View Source

    417    def take(self, count: int) -> Take[Item]:
    418        """Take the first number of items until the number of items
    419        are yielded or the end of the iterator is exhausted.
    420
    421        Example
    422        -------
    423        ```py
    424        iterator = Iter(['c', 'x', 'y'])
    425
    426        for x in iterator.take(2):
    427            assert x in ('c', 'x')
    428
    429        # <Iter(['c', 'x'])>
    430        ```
    431        """
    432        return Take(self, count)

Take the first number of items until the number of items are yielded or the end of the iterator is exhausted.

###### Example

    iterator = Iter(['c', 'x', 'y'])

    for x in iterator.take(2):
        assert x in ('c', 'x')

    # <Iter(['c', 'x'])>

def skip(self, count: int) -> Skip[~Item]: View Source

    434    def skip(self, count: int) -> Skip[Item]:
    435        """Skips the first number of items in the iterator.
    436
    437        Example
    438        -------
    439        ```py
    440        iterator = Iter((1, 2, 3, 4))
    441        for i in iterator.skip(2):
    442            print(i)
    443
    444        # 3
    445        # 4
    446        ```
    447        """
    448        return Skip(self, count)

Skips the first number of items in the iterator.

###### Example

    iterator = Iter((1, 2, 3, 4))
    for i in iterator.skip(2):
        print(i)

    # 3
    # 4

def enumerate(self, *, start: int = 0) -> Enumerate[~Item]: View Source

    450    def enumerate(self, *, start: int = 0) -> Enumerate[Item]:
    451        """Create a new iterator that yields a tuple of the index and item.
    452
    453        Example
    454        -------
    455        ```py
    456        iterator = Iter([1, 2, 3])
    457        for index, item in iterator.enumerate():
    458            print(index, item)
    459
    460        # 0 1
    461        # 1 2
    462        # 2 3
    463        ```
    464        """
    465        return Enumerate(self, start)

Create a new iterator that yields a tuple of the index and item.

###### Example

    iterator = Iter([1, 2, 3])
    for index, item in iterator.enumerate():
        print(index, item)

    # 0 1
    # 1 2
    # 2 3

def take_while(self, f: Callable[[~Item], bool]) -> TakeWhile[~Item]: View Source

    467    def take_while(self, f: collections.Callable[[Item], bool]) -> TakeWhile[Item]:
    468        """yields items from the iterator while predicate returns `True`.
    469
    470        The rest of the items are discarded as soon as the predicate returns `False`
    471
    472        Example
    473        -------
    474        ```py
    475        iterator = Iter(['a', 'ab', 'xd', 'ba'])
    476        for x in iterator.take_while(lambda x: 'a' in x):
    477            print(x)
    478
    479        # a
    480        # ab
    481        ```
    482
    483        Parameters
    484        ----------
    485        predicate: `collections.Callable[[Item], bool]`
    486            The function to predicate each item in the iterator.
    487        """
    488        return TakeWhile(self, f)

yields items from the iterator while predicate returns `True`.

The rest of the items are discarded as soon as the predicate returns `False`

###### Example

    iterator = Iter(['a', 'ab', 'xd', 'ba'])
    for x in iterator.take_while(lambda x: 'a' in x):
        print(x)

    # a
    # ab

###### Parameters

- predicate (`collections.Callable[[Item], bool]`): The function to predicate each item in the iterator.

def drop_while(self, f: Callable[[~Item], bool]) -> DropWhile[~Item]: View Source

    490    def drop_while(self, f: collections.Callable[[Item], bool]) -> DropWhile[Item]:
    491        """Yields items from the iterator while predicate returns `False`.
    492
    493        Example
    494        -------
    495        ```py
    496        iterator = Iter(['a', 'ab', 'xd', 'ba'])
    497        for x in iterator.drop_while(lambda x: 'a' in x):
    498            print(x)
    499
    500        # xd
    501        # ba
    502        ```
    503
    504        Parameters
    505        ----------
    506        predicate: `collections.Callable[[Item], bool]`
    507            The function to predicate each item in the iterator.
    508        """
    509        return DropWhile(self, f)

Yields items from the iterator while predicate returns `False`.

###### Example

    iterator = Iter(['a', 'ab', 'xd', 'ba'])
    for x in iterator.drop_while(lambda x: 'a' in x):
        print(x)

    # xd
    # ba

###### Parameters

- predicate (`collections.Callable[[Item], bool]`): The function to predicate each item in the iterator.

def chunks(self, chunk_size: int, /) -> Chunks[~Item]: View Source

    511    def chunks(self, chunk_size: int, /) -> Chunks[Item]:
    512        """Returns an iterator over `chunk_size` elements of the iterator at a time,
    513        starting at the beginning of the iterator.
    514
    515        Example
    516        -------
    517        ```py
    518        iter = Iter(['a', 'b', 'c', 'd', 'e'])
    519        chunks = iter.chunks()
    520        assert chunks.next().unwrap() == ['a', 'b']
    521        assert chunks.next().unwrap() == ['c', 'd']
    522        assert chunks.next().unwrap() == ['e']
    523        assert chunks.next().is_none()
    524        ```
    525        """
    526        return Chunks(self, chunk_size)

Returns an iterator over `chunk_size` elements of the iterator at a time, starting at the beginning of the iterator.

###### Example

    iter = Iter(['a', 'b', 'c', 'd', 'e'])
    chunks = iter.chunks()
    assert chunks.next().unwrap() == ['a', 'b']
    assert chunks.next().unwrap() == ['c', 'd']
    assert chunks.next().unwrap() == ['e']
    assert chunks.next().is_none()

def all(self, predicate: Callable[[~Item], bool]) -> bool: View Source

    528    def all(self, predicate: collections.Callable[[Item], bool]) -> bool:
    529        """Return `True` if all items in the iterator match the predicate.
    530
    531        Example
    532        -------
    533        ```py
    534        iterator = Iter([1, 2, 3])
    535        while iterator.all(lambda item: isinstance(item, int)):
    536            print("Still all integers")
    537            continue
    538            # Still all integers
    539        ```
    540
    541        Parameters
    542        ----------
    543        predicate: `collections.Callable[[Item], bool]`
    544            The function to test each item in the iterator.
    545        """
    546        return all(predicate(item) for item in self)

Return `True` if all items in the iterator match the predicate.

###### Example

    iterator = Iter([1, 2, 3])
    while iterator.all(lambda item: isinstance(item, int)):
        print("Still all integers")
        continue
        # Still all integers

###### Parameters

- predicate (`collections.Callable[[Item], bool]`): The function to test each item in the iterator.

def any(self, predicate: Callable[[~Item], bool]) -> bool: View Source

    548    def any(self, predicate: collections.Callable[[Item], bool]) -> bool:
    549        """`True` if any items in the iterator match the predicate.
    550
    551        Example
    552        -------
    553        ```py
    554        iterator = Iter([1, 2, 3])
    555        if iterator.any(lambda item: isinstance(item, int)):
    556            print("At least one item is an int.")
    557        # At least one item is an int.
    558        ```
    559
    560        Parameters
    561        ----------
    562        predicate: `collections.Callable[[Item], bool]`
    563            The function to test each item in the iterator.
    564        """
    565        return any(predicate(item) for item in self)

`True` if any items in the iterator match the predicate.

###### Example

    iterator = Iter([1, 2, 3])
    if iterator.any(lambda item: isinstance(item, int)):
        print("At least one item is an int.")
    # At least one item is an int.

###### Parameters

- predicate (`collections.Callable[[Item], bool]`): The function to test each item in the iterator.

def zip( self, other: Iterable[~OtherItem]) -> Iter[tuple[~Item, ~OtherItem]]: View Source

    567    def zip(
    568        self, other: collections.Iterable[OtherItem]
    569    ) -> Iter[tuple[Item, OtherItem]]:
    570        """Zips the iterator with another iterable.
    571
    572        Example
    573        -------
    574        ```py
    575        iterator = Iter([1, 2, 3])
    576        for item, other_item in iterator.zip([4, 5, 6]):
    577            assert item == other_item
    578        <Iter([(1, 4), (2, 5), (3, 6)])>
    579        ```
    580
    581        Parameters
    582        ----------
    583        other: `Iter[OtherItem]`
    584            The iterable to zip with.
    585
    586        Returns
    587        -------
    588        `Iter[tuple[Item, OtherItem]]`
    589            The zipped iterator.
    590
    591        """
    592        return Iter(zip(self.raw_parts(), other))

Zips the iterator with another iterable.

###### Example

    iterator = Iter([1, 2, 3])
    for item, other_item in iterator.zip([4, 5, 6]):
        assert item == other_item
    <Iter([(1, 4), (2, 5), (3, 6)])>

###### Parameters

- other (`Iter[OtherItem]`): The iterable to zip with.

###### Returns

- `Iter[tuple[Item, OtherItem]]`: The zipped iterator.

def sort( self, *, key: 'collections.Callable[[Item], _typeshed.SupportsRichComparison]', reverse: bool = False) -> Iter[~Item]: View Source

    594    def sort(
    595        self,
    596        *,
    597        key: collections.Callable[[Item], _typeshed.SupportsRichComparison],
    598        reverse: bool = False,
    599    ) -> Iter[Item]:
    600        """Sorts the iterator.
    601
    602        Example
    603        -------
    604        ```py
    605        iterator = Iter([3, 1, 6, 7])
    606        for item in iterator.sort(key=lambda item: item < 3):
    607            print(item)
    608        # 1
    609        # 3
    610        # 6
    611        # 7
    612        ```
    613
    614        Parameters
    615        ----------
    616        key: `collections.Callable[[Item], Any]`
    617            The function to sort by.
    618        reverse: `bool`
    619            Whether to reverse the sort.
    620        """
    621        return Iter(sorted(self.raw_parts(), key=key, reverse=reverse))

Sorts the iterator.

###### Example

    iterator = Iter([3, 1, 6, 7])
    for item in iterator.sort(key=lambda item: item < 3):
        print(item)
    # 1
    # 3
    # 6
    # 7

###### Parameters

- key (`collections.Callable[[Item], Any]`): The function to sort by.
- reverse (`bool`): Whether to reverse the sort.

def reversed(self) -> Iter[~Item]: View Source

    623    def reversed(self) -> Iter[Item]:
    624        """Returns a new iterator that yields the items in the iterator in reverse order.
    625
    626        This consumes this iterator into a sequence and return a new iterator containing all of the elements
    627        in reversed order.
    628
    629        Example
    630        -------
    631        ```py
    632        iterator = Iter([3, 1, 6, 7])
    633        for item in iterator.reversed():
    634            print(item)
    635        # 7
    636        # 6
    637        # 1
    638        # 3
    639        ```
    640        """
    641        # NOTE: In order to reverse the iterator we need to
    642        # first collect it into some collection.
    643        return Iter(reversed(list(_ for _ in self)))

Returns a new iterator that yields the items in the iterator in reverse order.

This consumes this iterator into a sequence and return a new iterator containing all of the elements in reversed order.

###### Example

    iterator = Iter([3, 1, 6, 7])
    for item in iterator.reversed():
        print(item)
    # 7
    # 6
    # 1
    # 3

def union(self, other: Iterable[~Item]) -> Iter[~Item]: View Source

    645    def union(self, other: collections.Iterable[Item]) -> Iter[Item]:
    646        """Returns a new iterator that yields all items from both iterators.
    647
    648        Example
    649        -------
    650        ```py
    651        iterator = Iter([1, 2, 3])
    652        other = [4, 5, 6]
    653
    654        for item in iterator.union(other):
    655            print(item)
    656        # 1
    657        # 2
    658        # 3
    659        # 4
    660        # 5
    661        # 6
    662        ```
    663
    664        Parameters
    665        ----------
    666        other: `Iter[Item]`
    667            The iterable to union with.
    668        """
    669        return Iter(itertools.chain(self.raw_parts(), other))

Returns a new iterator that yields all items from both iterators.

###### Example

    iterator = Iter([1, 2, 3])
    other = [4, 5, 6]

    for item in iterator.union(other):
        print(item)
    # 1
    # 2
    # 3
    # 4
    # 5
    # 6

###### Parameters

- other (`Iter[Item]`): The iterable to union with.

def first(self) -> 'Option[Item]': View Source

    671    def first(self) -> Option[Item]:
    672        """Returns the first item in the iterator.
    673
    674        Example
    675        -------
    676        ```py
    677        iterator = Iter([3, 1, 6, 7])
    678        iterator.first().is_some_and(lambda x: x == 3)
    679        ```
    680        """
    681        return self.take(1).next()

Returns the first item in the iterator.

###### Example

    iterator = Iter([3, 1, 6, 7])
    iterator.first().is_some_and(lambda x: x == 3)

def last(self) -> 'Option[Item]': View Source

    683    def last(self) -> Option[Item]:
    684        """Returns the last item in the iterator.
    685
    686        Example
    687        -------
    688        ```py
    689        iterator = Iter([3, 1, 6, 7])
    690        iterator.last().is_some_and(lambda x: x == 7)
    691        ```
    692        """
    693        return self.reversed().first()

Returns the last item in the iterator.

###### Example

    iterator = Iter([3, 1, 6, 7])
    iterator.last().is_some_and(lambda x: x == 7)

def count(self) -> int: View Source

    695    def count(self) -> int:
    696        """Return the count of elements in memory this iterator has.
    697
    698        Example
    699        -------
    700        ```py
    701        it = Iter(range(3))
    702        assert it.count() == 3
    703        ```
    704        """
    705        count = 0
    706        for _ in self:
    707            count += 1
    708
    709        return count

Return the count of elements in memory this iterator has.

###### Example

    it = Iter(range(3))
    assert it.count() == 3

def find(self, predicate: Callable[[~Item], bool]) -> 'Option[Item]': View Source

    711    def find(self, predicate: collections.Callable[[Item], bool]) -> Option[Item]:
    712        """Searches for an element of an iterator that satisfies a predicate.
    713
    714        If you want the position of the element, use `Iterator.position` instead.
    715
    716        `find()` takes a lambda that returns true or false. It applies this closure to each element of the iterator,
    717        and if any of them return true, then find() returns `Some(element)`. If they all return false, it returns None.
    718
    719        Example
    720        -------
    721        ```py
    722        it = Iter(range(10))
    723        item = it.find(lambda num: num > 5)
    724        print(item) # 6
    725        ```
    726        """
    727        for item in self:
    728            if predicate(item):
    729                return _option.Some(item)
    730
    731        # no more items
    732        return _option.NOTHING

Searches for an element of an iterator that satisfies a predicate.

If you want the position of the element, use `Iterator.position` instead.

`find()` takes a lambda that returns true or false. It applies this closure to each element of the iterator, and if any of them return true, then find() returns `Some(element)`. If they all return false, it returns None.

###### Example

    it = Iter(range(10))
    item = it.find(lambda num: num > 5)
    print(item) # 6

def position(self, predicate: Callable[[~Item], bool]) -> 'Option[int]': View Source

    734    def position(self, predicate: collections.Callable[[Item], bool]) -> Option[int]:
    735        """Searches for the position of an element in the iterator that satisfies a predicate.
    736
    737        If you want the object itself, use `Iterator.find` instead.
    738
    739        `position()` takes a lambda that returns true or false. It applies this closure to each element of the iterator,
    740        and if any of them return true, then position() returns `Some(position_of_element)`. If they all return false, it returns None.
    741
    742        Example
    743        -------
    744        ```py
    745        it = Iter(range(10))
    746        position = it.find(lambda num: num > 5)
    747        assert position.unwrap() == 6
    748        ```
    749        """
    750        for position, value in self.enumerate():
    751            if predicate(value):
    752                return _option.Some(position)
    753
    754        # no more items
    755        return _option.NOTHING

Searches for the position of an element in the iterator that satisfies a predicate.

If you want the object itself, use `Iterator.find` instead.

`position()` takes a lambda that returns true or false. It applies this closure to each element of the iterator, and if any of them return true, then position() returns `Some(position_of_element)`. If they all return false, it returns None.

###### Example

    it = Iter(range(10))
    position = it.find(lambda num: num > 5)
    assert position.unwrap() == 6

def fold( self, init: ~OtherItem, f: Callable[[~OtherItem, ~Item], ~OtherItem]) -> ~OtherItem: View Source

    757    def fold(
    758        self, init: OtherItem, f: collections.Callable[[OtherItem, Item], OtherItem]
    759    ) -> OtherItem:
    760        """Folds every element into an accumulator by applying an operation, returning the final result.
    761
    762        fold() takes two arguments: an initial value, and a closure with two arguments: an ‘accumulator’, and an element.
    763        The closure returns the value that the accumulator should have for the next iteration.
    764
    765        The initial value is the value the accumulator will have on the first call.
    766
    767        After applying this closure to every element of the iterator, fold() returns the accumulator.
    768
    769        This operation is sometimes called ‘reduce’ or ‘inject’.
    770
    771        Example
    772        -------
    773        ```py
    774        a = Iter([1, 2, 3, 4])
    775        sum = a.fold(0, lambda acc, elem: acc + elem)
    776        assert sum == 10
    777        ```
    778        """
    779        accum = init
    780        while True:
    781            try:
    782                x = self.__next__()
    783                accum = f(accum, x)
    784            except StopIteration:
    785                break
    786
    787        return accum

Folds every element into an accumulator by applying an operation, returning the final result.

fold() takes two arguments: an initial value, and a closure with two arguments: an ‘accumulator’, and an element. The closure returns the value that the accumulator should have for the next iteration.

The initial value is the value the accumulator will have on the first call.

After applying this closure to every element of the iterator, fold() returns the accumulator.

This operation is sometimes called ‘reduce’ or ‘inject’.

###### Example

    a = Iter([1, 2, 3, 4])
    sum = a.fold(0, lambda acc, elem: acc + elem)
    assert sum == 10

def advance_by(self, n: int) -> '_result.Result[None, int]': View Source

    789    def advance_by(self, n: int) -> _result.Result[None, int]:
    790        """Advances the iterator by `n` elements.
    791
    792        Returns `Result[None, int]`, where `Ok(None)` means the iterator
    793        advanced successfully, and `Err(int)` if `None` encountered, where `int`
    794        represents the remaining number of steps that could not be advanced because the iterator ran out.
    795
    796        Example
    797        -------
    798        ```py
    799        it = into_iter([1, 2, 3, 4])
    800        assert it.advance_by(2).is_ok()
    801        assert it.next() == Some(3)
    802        assert it.advance_by(0).is_ok()
    803        assert it.advance_by(100) == Err(99)
    804        ```
    805        """
    806        for i in range(n):
    807            try:
    808                self.__next__()
    809            except StopIteration:
    810                return _result.Err(n - i)
    811
    812        return _result.Ok(None)

Advances the iterator by `n` elements.

Returns `Result[None, int]`, where `Ok(None)` means the iterator advanced successfully, and `Err(int)` if `None` encountered, where `int` represents the remaining number of steps that could not be advanced because the iterator ran out.

###### Example

    it = into_iter([1, 2, 3, 4])
    assert it.advance_by(2).is_ok()
    assert it.next() == Some(3)
    assert it.advance_by(0).is_ok()
    assert it.advance_by(100) == Err(99)

def nth(self, n: int) -> 'Option[Item]': View Source

    814    def nth(self, n: int) -> Option[Item]:
    815        """Returns the `n`th element of the iterator
    816
    817        Just like normal indexing, the count `n` starts from zero, so `nth(0)` returns the first
    818        value.
    819
    820        Note all elements before `n` will be skipped / consumed.
    821
    822        Example
    823        -------
    824        ```py
    825        a = into_iter([1, 2, 3])
    826        assert a.iter().nth(1) == Some(2)
    827        ```
    828        """
    829        for _ in range(n):
    830            try:
    831                self.__next__()
    832            except StopIteration:
    833                return _option.NOTHING
    834
    835        return self.next()

Returns the `n`th element of the iterator

Just like normal indexing, the count `n` starts from zero, so `nth(0)` returns the first value.

Note all elements before `n` will be skipped / consumed.

###### Example

    a = into_iter([1, 2, 3])
    assert a.iter().nth(1) == Some(2)

def sum(self: 'Sum') -> int: View Source

    837    def sum(self: Sum) -> int:
    838        """Sums an iterator of a possible type `T` that can be converted to an integer.
    839
    840        where `T` is a typeof (`int`, `float`, `str`, `ReadableBuffer`, `SupportsTrunc`, `SupportsIndex`).
    841
    842        Example
    843        -------
    844        ```py
    845        numbers: Iterator[str] = Iter(["1", "2", "3"])
    846        total = numbers.sum()
    847        assert total == 6
    848        ```
    849        """
    850        return sum(int(_) for _ in self)

Sums an iterator of a possible type `T` that can be converted to an integer.

where `T` is a typeof (`int`, `float`, `str`, `ReadableBuffer`, `SupportsTrunc`, `SupportsIndex`).

###### Example

    numbers: Iterator[str] = Iter(["1", "2", "3"])
    total = numbers.sum()
    assert total == 6

def for_each(self, func: Callable[[~Item], typing.Any]) -> None: View Source

    852    def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None:
    853        """Calls `func` on each item in the iterator.
    854
    855        Example
    856        -------
    857        ```py
    858        iterator = Iter([1, 2, 3])
    859        iterator.for_each(lambda item: print(item))
    860        # 1
    861        # 2
    862        # 3
    863        ```
    864
    865        Parameters
    866        ----------
    867        func: `collections.Callable[[Item], typing.Any]`
    868            The function to call on each item in the iterator.
    869        """
    870        for item in self:
    871            func(item)

Calls `func` on each item in the iterator.

###### Example

    iterator = Iter([1, 2, 3])
    iterator.for_each(lambda item: print(item))
    # 1
    # 2
    # 3

###### Parameters

- func (`collections.Callable[[Item], typing.Any]`): The function to call on each item in the iterator.

async def async_for_each( self, func: Callable[[~Item], Coroutine[typing.Any, typing.Any, ~OtherItem]]) -> '_result.Result[collections.Sequence[OtherItem], futures.JoinError]': View Source

    873    async def async_for_each(
    874        self,
    875        func: collections.Callable[
    876            [Item], collections.Coroutine[typing.Any, typing.Any, OtherItem]
    877        ],
    878    ) -> _result.Result[collections.Sequence[OtherItem], futures.JoinError]:
    879        """Calls the async function on each item in the iterator *concurrently*.
    880
    881        Concurrently meaning that the next item will not wait for other items
    882        to finish to execute, each item gets called in a separate task.
    883
    884        After all the tasks finish, a `Result[list[T], JoinError]` will be returned,
    885        which will need to be handled by the caller.
    886
    887        Example
    888        -------
    889        ```py
    890        async def create_user(username: str) -> None:
    891            await aiohttp.request("POST", f'.../{username}')
    892
    893        async def main():
    894            users = sain.into_iter(["Danny", "Flower"])
    895            match await users.async_for_each(lambda username: create_user(username)):
    896                case Ok(result):
    897                    # all good
    898                case Err(why):
    899                    print(f"couldn't gather all futures, err={why}")
    900        ```
    901
    902        Parameters
    903        ----------
    904        func: `collections.Callable[[Item], Coroutine[None, Any, Any]]`
    905            The async function to call on each item in the iterator.
    906        """
    907        return await futures.join(*(func(item) for item in self))

Calls the async function on each item in the iterator concurrently.

Concurrently meaning that the next item will not wait for other items to finish to execute, each item gets called in a separate task.

After all the tasks finish, a `Result[list[T], JoinError]` will be returned, which will need to be handled by the caller.

###### Example

    async def create_user(username: str) -> None:
        await aiohttp.request("POST", f'.../{username}')

    async def main():
        users = sain.into_iter(["Danny", "Flower"])
        match await users.async_for_each(lambda username: create_user(username)):
            case Ok(result):
                # all good
            case Err(why):
                print(f"couldn't gather all futures, err={why}")

###### Parameters

- func (`collections.Callable[[Item], Coroutine[None, Any, Any]]`): The async function to call on each item in the iterator.

@typing.final
class TrustedIter(typing.Generic[~Item], sain.iter.ExactSizeIterator[~Item]): View Source

    1070@typing.final
    1071class TrustedIter(typing.Generic[Item], ExactSizeIterator[Item]):
    1072    """Similar to `Iter`, but it reports an accurate length using `ExactSizeIterator`.
    1073
    1074    iterable objects such as `Vec`, `Bytes`, `list` and other `Sized` may be created
    1075    using this iterator.
    1076
    1077    Example
    1078    -------
    1079    ```py
    1080    # we know the size of the iterator.
    1081    sized_buf: TrustedIter[int] = into_iter((1, 2, 3, 4))
    1082    # this is `Iter[int]` since we don't know when the generator will stop yielding.
    1083    unsized_buf: Iter[int] = into_iter((_ for _ in ([1, 2, 3, 4] if cond else [1, 2])))
    1084    ```
    1085
    1086    Parameters
    1087    ----------
    1088    items: `collections.Collection[Item]`
    1089        A sized collection of items to iterate over.
    1090    """
    1091
    1092    __slots__ = ("_it", "_len", "__alive")
    1093
    1094    def __init__(self, iterable: collections.Sequence[Item]) -> None:
    1095        self.__alive = iterable
    1096        self._len = len(iterable)
    1097        self._it = iter(iterable)
    1098
    1099    @property
    1100    def __slice_checked_get(self) -> collections.Sequence[Item] | None:
    1101        try:
    1102            return self.__alive
    1103        except AttributeError:
    1104            return None
    1105
    1106    def next(self) -> Option[Item]:
    1107        if self._len == 0:
    1108            # ! SAFETY: len == 0
    1109            return _option.NOTHING
    1110
    1111        return _option.Some(self.__next__())
    1112
    1113    @unsafe
    1114    def next_unchecked(self) -> Item:
    1115        """Returns the next item in the iterator without checking if it exists.
    1116
    1117        This is equivalent to calling `next()` on the iterator directly.
    1118
    1119        Example
    1120        -------
    1121        ```py
    1122        iterator = Iter([1])
    1123        assert iterator.next_unchecked() == 1
    1124        iterator.next_unchecked() # raises StopIteration
    1125        ```
    1126        """
    1127        return self.__next__()
    1128
    1129    @unsafe
    1130    def set_len(self, new_len: int) -> None:
    1131        """Sets the length of the iterator to `new_len`.
    1132
    1133        This is unsafe and should only be used if you know what you're doing.
    1134
    1135        Example
    1136        -------
    1137        ```py
    1138        iterator = Iter([1, 2, 3])
    1139        iterator.set_len(2)
    1140        assert iterator.len() == 2
    1141        ```
    1142        """
    1143        self._len = new_len
    1144
    1145    def as_slice(self) -> Slice[Item]:
    1146        """Returns an immutable slice of all elements that have not been yielded
    1147
    1148        Example
    1149        -------
    1150        ```py
    1151        iterator = into_iter([1, 2, 3])
    1152        iterator.as_slice() == [1, 2, 3]
    1153        iterator.next()
    1154        assert iterator.as_slice() == [2, 3]
    1155        ```
    1156        """
    1157        from .collections.slice import Slice
    1158
    1159        return Slice(self.__slice_checked_get or ())
    1160
    1161    def __repr__(self) -> str:
    1162        # __alive is dropped from `self`.
    1163        if (s := self.__slice_checked_get) is None:
    1164            return "TrustedIter(<empty>)"
    1165
    1166        return f"TrustedIter({s[-self._len :]})"
    1167
    1168    def __next__(self) -> Item:
    1169        try:
    1170            i = next(self._it)
    1171        except StopIteration:
    1172            # don't reference this anymore.
    1173            del self.__alive
    1174            raise
    1175
    1176        self._len -= 1
    1177        return i
    1178
    1179    def __getitem__(self, index: int) -> Item:
    1180        if self._len == 0:
    1181            raise IndexError("index out of bounds")
    1182
    1183        return self.skip(index).first().unwrap_or_else(oob)
    1184
    1185    def __contains__(self, item: Item) -> bool:
    1186        return item in self._it
    1187
    1188    def __len__(self) -> int:
    1189        return self._len

Similar to `Iter`, but it reports an accurate length using `ExactSizeIterator`.

iterable objects such as `Vec`, `Bytes`, `list` and other `Sized` may be created using this iterator.

###### Example

    # we know the size of the iterator.
    sized_buf: TrustedIter[int] = into_iter((1, 2, 3, 4))
    # this is `Iter[int]` since we don't know when the generator will stop yielding.
    unsized_buf: Iter[int] = into_iter((_ for _ in ([1, 2, 3, 4] if cond else [1, 2])))

###### Parameters

- items (`collections.Collection[Item]`): A sized collection of items to iterate over.

TrustedIter(iterable: Sequence[~Item]) View Source

    1094    def __init__(self, iterable: collections.Sequence[Item]) -> None:
    1095        self.__alive = iterable
    1096        self._len = len(iterable)
    1097        self._it = iter(iterable)

def next(self) -> 'Option[Item]': View Source

    1106    def next(self) -> Option[Item]:
    1107        if self._len == 0:
    1108            # ! SAFETY: len == 0
    1109            return _option.NOTHING
    1110
    1111        return _option.Some(self.__next__())

Advance the iterator, Returning the next item, `Some(None)` if all items yielded.

###### Example

    iterator = Iter(["1", "2"])
    assert iterator.next() == Some("1")
    assert iterator.next() == Some("2")
    assert iterator.next().is_none()

@unsafe
def next_unchecked(self) -> ~Item: View Source

    1113    @unsafe
    1114    def next_unchecked(self) -> Item:
    1115        """Returns the next item in the iterator without checking if it exists.
    1116
    1117        This is equivalent to calling `next()` on the iterator directly.
    1118
    1119        Example
    1120        -------
    1121        ```py
    1122        iterator = Iter([1])
    1123        assert iterator.next_unchecked() == 1
    1124        iterator.next_unchecked() # raises StopIteration
    1125        ```
    1126        """
    1127        return self.__next__()

Returns the next item in the iterator without checking if it exists.

This is equivalent to calling `next()` on the iterator directly.

###### Example

    iterator = Iter([1])
    assert iterator.next_unchecked() == 1
    iterator.next_unchecked() # raises StopIteration

# Safety ⚠️

Calling this method without knowing the output is considered undefined behavior.

@unsafe
def set_len(self, new_len: int) -> None: View Source

    1129    @unsafe
    1130    def set_len(self, new_len: int) -> None:
    1131        """Sets the length of the iterator to `new_len`.
    1132
    1133        This is unsafe and should only be used if you know what you're doing.
    1134
    1135        Example
    1136        -------
    1137        ```py
    1138        iterator = Iter([1, 2, 3])
    1139        iterator.set_len(2)
    1140        assert iterator.len() == 2
    1141        ```
    1142        """
    1143        self._len = new_len

Sets the length of the iterator to `new_len`.

This is unsafe and should only be used if you know what you're doing.

###### Example

    iterator = Iter([1, 2, 3])
    iterator.set_len(2)
    assert iterator.len() == 2

# Safety ⚠️

Calling this method without knowing the output is considered undefined behavior.

def as_slice(self) -> 'Slice[Item]': View Source

    1145    def as_slice(self) -> Slice[Item]:
    1146        """Returns an immutable slice of all elements that have not been yielded
    1147
    1148        Example
    1149        -------
    1150        ```py
    1151        iterator = into_iter([1, 2, 3])
    1152        iterator.as_slice() == [1, 2, 3]
    1153        iterator.next()
    1154        assert iterator.as_slice() == [2, 3]
    1155        ```
    1156        """
    1157        from .collections.slice import Slice
    1158
    1159        return Slice(self.__slice_checked_get or ())

Returns an immutable slice of all elements that have not been yielded

###### Example

    iterator = into_iter([1, 2, 3])
    iterator.as_slice() == [1, 2, 3]
    iterator.next()
    assert iterator.as_slice() == [2, 3]

##### Inherited Members

ExactSizeIterator
  count
  len
  is_empty
Iterator
  default
  collect
  collect_into
  to_vec
  sink
  raw_parts
  cloned
  copied
  map
  filter
  take
  skip
  enumerate
  take_while
  drop_while
  chunks
  all
  any
  zip
  sort
  reversed
  union
  first
  last
  find
  position
  fold
  advance_by
  nth
  sum
  for_each
  async_for_each

@diagnostic
class Cloned(typing.Generic[~Item], sain.iter.Iterator[~Item]): View Source

    1192@diagnostic
    1193class Cloned(typing.Generic[Item], Iterator[Item]):
    1194    """An iterator that copies the elements from an underlying iterator.
    1195
    1196    This iterator is created by the `Iterator.cloned` method.
    1197    """
    1198
    1199    __slots__ = ("_it",)
    1200
    1201    def __init__(self, it: Iterator[Item]) -> None:
    1202        self._it = it
    1203
    1204    def __next__(self) -> Item:
    1205        n = self._it.__next__()
    1206
    1207        # Avoid useless function call for a list.
    1208        if isinstance(n, list):
    1209            # SAFETY: We know this is a list.
    1210            return n[:]  # pyright: ignore
    1211
    1212        return copy.copy(n)

An iterator that copies the elements from an underlying iterator.

This iterator is created by the `Iterator.cloned` method.

Cloned(it: Iterator[~Item]) View Source

    1201    def __init__(self, it: Iterator[Item]) -> None:
    1202        self._it = it

##### Inherited Members

Iterator
  default
  collect
  collect_into
  to_vec
  sink
  raw_parts
  next
  cloned
  copied
  map
  filter
  take
  skip
  enumerate
  take_while
  drop_while
  chunks
  all
  any
  zip
  sort
  reversed
  union
  first
  last
  count
  find
  position
  fold
  advance_by
  nth
  sum
  for_each
  async_for_each

@diagnostic
class Copied(typing.Generic[~Item], sain.iter.Iterator[~Item]): View Source

    1215@diagnostic
    1216class Copied(typing.Generic[Item], Iterator[Item]):
    1217    """An iterator that deeply-copies the elements from an underlying iterator.
    1218
    1219    This iterator is created by the `Iterator.copied` method.
    1220    """
    1221
    1222    __slots__ = ("_it",)
    1223
    1224    def __init__(self, it: Iterator[Item]) -> None:
    1225        self._it = it
    1226
    1227    def __next__(self) -> Item:
    1228        return copy.deepcopy(self._it.__next__())

An iterator that deeply-copies the elements from an underlying iterator.

This iterator is created by the `Iterator.copied` method.

Copied(it: Iterator[~Item]) View Source

    1224    def __init__(self, it: Iterator[Item]) -> None:
    1225        self._it = it

##### Inherited Members

Iterator
  default
  collect
  collect_into
  to_vec
  sink
  raw_parts
  next
  cloned
  copied
  map
  filter
  take
  skip
  enumerate
  take_while
  drop_while
  chunks
  all
  any
  zip
  sort
  reversed
  union
  first
  last
  count
  find
  position
  fold
  advance_by
  nth
  sum
  for_each
  async_for_each

@diagnostic
class Take(typing.Generic[~Item], sain.iter.Iterator[~Item]): View Source

    1273@diagnostic
    1274class Take(typing.Generic[Item], Iterator[Item]):
    1275    """An iterator that yields the first `number` of elements and drops the rest.
    1276
    1277    This iterator is created by the `Iterator.take` method.
    1278    """
    1279
    1280    __slots__ = ("_it", "_taken", "_count")
    1281
    1282    def __init__(self, it: Iterator[Item], count: int) -> None:
    1283        if count <= 0:
    1284            raise ValueError("`count` must be non-zero")
    1285
    1286        self._it = it
    1287        self._taken = count
    1288        self._count = 0
    1289
    1290    def __next__(self) -> Item:
    1291        if self._count >= self._taken:
    1292            unreachable()
    1293
    1294        item = self._it.__next__()
    1295        self._count += 1
    1296        return item

An iterator that yields the first `number` of elements and drops the rest.

This iterator is created by the `Iterator.take` method.

Take(it: Iterator[~Item], count: int) View Source

    1282    def __init__(self, it: Iterator[Item], count: int) -> None:
    1283        if count <= 0:
    1284            raise ValueError("`count` must be non-zero")
    1285
    1286        self._it = it
    1287        self._taken = count
    1288        self._count = 0

##### Inherited Members

Iterator
  default
  collect
  collect_into
  to_vec
  sink
  raw_parts
  next
  cloned
  copied
  map
  filter
  take
  skip
  enumerate
  take_while
  drop_while
  chunks
  all
  any
  zip
  sort
  reversed
  union
  first
  last
  count
  find
  position
  fold
  advance_by
  nth
  sum
  for_each
  async_for_each

@diagnostic
class Filter(typing.Generic[~Item], sain.iter.Iterator[~Item]): View Source

    1250@diagnostic
    1251class Filter(typing.Generic[Item], Iterator[Item]):
    1252    """An iterator that filters the elements to a `predicate`.
    1253
    1254    This iterator is created by the `Iterator.filter` method.
    1255    """
    1256
    1257    __slots__ = ("_it", "_call")
    1258
    1259    def __init__(
    1260        self, it: Iterator[Item], call: collections.Callable[[Item], bool]
    1261    ) -> None:
    1262        self._it = it
    1263        self._call = call
    1264
    1265    def __next__(self) -> Item:
    1266        for item in self._it:
    1267            if self._call(item):
    1268                return item
    1269
    1270        unreachable()

An iterator that filters the elements to a `predicate`.

This iterator is created by the `Iterator.filter` method.

Filter(it: Iterator[~Item], call: Callable[[~Item], bool]) View Source

    1259    def __init__(
    1260        self, it: Iterator[Item], call: collections.Callable[[Item], bool]
    1261    ) -> None:
    1262        self._it = it
    1263        self._call = call

##### Inherited Members

Iterator
  default
  collect
  collect_into
  to_vec
  sink
  raw_parts
  next
  cloned
  copied
  map
  filter
  take
  skip
  enumerate
  take_while
  drop_while
  chunks
  all
  any
  zip
  sort
  reversed
  union
  first
  last
  count
  find
  position
  fold
  advance_by
  nth
  sum
  for_each
  async_for_each

@diagnostic
class Map(typing.Generic[~Item, ~OtherItem], sain.iter.Iterator[~OtherItem]): View Source

    1231@diagnostic
    1232class Map(typing.Generic[Item, OtherItem], Iterator[OtherItem]):
    1233    """An iterator that maps the elements to a callable.
    1234
    1235    This iterator is created by the `Iterator.map` method.
    1236    """
    1237
    1238    __slots__ = ("_it", "_call")
    1239
    1240    def __init__(
    1241        self, it: Iterator[Item], call: collections.Callable[[Item], OtherItem]
    1242    ) -> None:
    1243        self._it = it
    1244        self._call = call
    1245
    1246    def __next__(self) -> OtherItem:
    1247        return self._call(self._it.__next__())

An iterator that maps the elements to a callable.

This iterator is created by the `Iterator.map` method.

Map(it: Iterator[~Item], call: Callable[[~Item], ~OtherItem]) View Source

    1240    def __init__(
    1241        self, it: Iterator[Item], call: collections.Callable[[Item], OtherItem]
    1242    ) -> None:
    1243        self._it = it
    1244        self._call = call

##### Inherited Members

Iterator
  default
  collect
  collect_into
  to_vec
  sink
  raw_parts
  next
  cloned
  copied
  map
  filter
  take
  skip
  enumerate
  take_while
  drop_while
  chunks
  all
  any
  zip
  sort
  reversed
  union
  first
  last
  count
  find
  position
  fold
  advance_by
  nth
  sum
  for_each
  async_for_each

@diagnostic
class Skip(typing.Generic[~Item], sain.iter.Iterator[~Item]): View Source

    1299@diagnostic
    1300class Skip(typing.Generic[Item], Iterator[Item]):
    1301    """An iterator that skips the first `number` of elements and yields the rest.
    1302
    1303    This iterator is created by the `Iterator.skip` method.
    1304    """
    1305
    1306    __slots__ = ("_it", "_count", "_skipped")
    1307
    1308    def __init__(self, it: Iterator[Item], count: int) -> None:
    1309        if count <= 0:
    1310            raise ValueError("`count` must be non-zero")
    1311
    1312        self._it = it
    1313        self._count = count
    1314        self._skipped = 0
    1315
    1316    def __next__(self) -> Item:
    1317        while self._skipped < self._count:
    1318            self._skipped += 1
    1319            self._it.__next__()
    1320
    1321        return self._it.__next__()

An iterator that skips the first `number` of elements and yields the rest.

This iterator is created by the `Iterator.skip` method.

Skip(it: Iterator[~Item], count: int) View Source

    1308    def __init__(self, it: Iterator[Item], count: int) -> None:
    1309        if count <= 0:
    1310            raise ValueError("`count` must be non-zero")
    1311
    1312        self._it = it
    1313        self._count = count
    1314        self._skipped = 0

##### Inherited Members

Iterator
  default
  collect
  collect_into
  to_vec
  sink
  raw_parts
  next
  cloned
  copied
  map
  filter
  take
  skip
  enumerate
  take_while
  drop_while
  chunks
  all
  any
  zip
  sort
  reversed
  union
  first
  last
  count
  find
  position
  fold
  advance_by
  nth
  sum
  for_each
  async_for_each

@diagnostic
class Enumerate(typing.Generic[~Item], sain.iter.Iterator[tuple[int, ~Item]]): View Source

    1324@diagnostic
    1325class Enumerate(typing.Generic[Item], Iterator[tuple[int, Item]]):
    1326    """An iterator that yields the current count and the element during iteration.
    1327
    1328    This iterator is created by the `Iterator.enumerate` method.
    1329    """
    1330
    1331    __slots__ = ("_it", "_count")
    1332
    1333    def __init__(self, it: Iterator[Item], start: int) -> None:
    1334        self._it = it
    1335        self._count = start
    1336
    1337    def __next__(self) -> tuple[int, Item]:
    1338        a = self._it.__next__()
    1339        i = self._count
    1340        self._count += 1
    1341        return i, a

An iterator that yields the current count and the element during iteration.

This iterator is created by the `Iterator.enumerate` method.

Enumerate(it: Iterator[~Item], start: int) View Source

    1333    def __init__(self, it: Iterator[Item], start: int) -> None:
    1334        self._it = it
    1335        self._count = start

##### Inherited Members

Iterator
  default
  collect
  collect_into
  to_vec
  sink
  raw_parts
  next
  cloned
  copied
  map
  filter
  take
  skip
  enumerate
  take_while
  drop_while
  chunks
  all
  any
  zip
  sort
  reversed
  union
  first
  last
  count
  find
  position
  fold
  advance_by
  nth
  sum
  for_each
  async_for_each

@diagnostic
class TakeWhile(typing.Generic[~Item], sain.iter.Iterator[~Item]): View Source

    1344@diagnostic
    1345class TakeWhile(typing.Generic[Item], Iterator[Item]):
    1346    """An iterator that yields elements while `predicate` returns `True`.
    1347
    1348    This iterator is created by the `Iterator.take_while` method.
    1349    """
    1350
    1351    __slots__ = ("_it", "_predicate")
    1352
    1353    def __init__(
    1354        self, it: Iterator[Item], predicate: collections.Callable[[Item], bool]
    1355    ) -> None:
    1356        self._it = it
    1357        self._predicate = predicate
    1358
    1359    def __next__(self) -> Item:
    1360        item = self._it.__next__()
    1361
    1362        if self._predicate(item):
    1363            return item
    1364
    1365        unreachable()

An iterator that yields elements while `predicate` returns `True`.

This iterator is created by the `Iterator.take_while` method.

TakeWhile(it: Iterator[~Item], predicate: Callable[[~Item], bool]) View Source

    1353    def __init__(
    1354        self, it: Iterator[Item], predicate: collections.Callable[[Item], bool]
    1355    ) -> None:
    1356        self._it = it
    1357        self._predicate = predicate

##### Inherited Members

Iterator
  default
  collect
  collect_into
  to_vec
  sink
  raw_parts
  next
  cloned
  copied
  map
  filter
  take
  skip
  enumerate
  take_while
  drop_while
  chunks
  all
  any
  zip
  sort
  reversed
  union
  first
  last
  count
  find
  position
  fold
  advance_by
  nth
  sum
  for_each
  async_for_each

@diagnostic
class DropWhile(typing.Generic[~Item], sain.iter.Iterator[~Item]): View Source

    1368@diagnostic
    1369class DropWhile(typing.Generic[Item], Iterator[Item]):
    1370    """An iterator that yields elements while `predicate` returns `False`.
    1371
    1372    This iterator is created by the `Iterator.drop_while` method.
    1373    """
    1374
    1375    __slots__ = ("_it", "_predicate", "_dropped")
    1376
    1377    def __init__(
    1378        self, it: Iterator[Item], predicate: collections.Callable[[Item], bool]
    1379    ) -> None:
    1380        self._it = it
    1381        self._predicate = predicate
    1382        self._dropped = False
    1383
    1384    def __next__(self) -> Item:
    1385        if not self._dropped:
    1386            while not self._predicate(item := self._it.__next__()):
    1387                pass
    1388
    1389            self._dropped = True
    1390            return item
    1391
    1392        unreachable()

An iterator that yields elements while `predicate` returns `False`.

This iterator is created by the `Iterator.drop_while` method.

DropWhile(it: Iterator[~Item], predicate: Callable[[~Item], bool]) View Source

    1377    def __init__(
    1378        self, it: Iterator[Item], predicate: collections.Callable[[Item], bool]
    1379    ) -> None:
    1380        self._it = it
    1381        self._predicate = predicate
    1382        self._dropped = False

##### Inherited Members

Iterator
  default
  collect
  collect_into
  to_vec
  sink
  raw_parts
  next
  cloned
  copied
  map
  filter
  take
  skip
  enumerate
  take_while
  drop_while
  chunks
  all
  any
  zip
  sort
  reversed
  union
  first
  last
  count
  find
  position
  fold
  advance_by
  nth
  sum
  for_each
  async_for_each

@diagnostic
class Chunks(typing.Generic[~Item], sain.iter.Iterator[collections.abc.Sequence[~Item]]): View Source

    1395@diagnostic
    1396class Chunks(typing.Generic[Item], Iterator[collections.Sequence[Item]]):
    1397    """An iterator that yields elements in chunks.
    1398
    1399    This iterator is created by the `Iterator.chunks` method.
    1400    """
    1401
    1402    __slots__ = ("chunk_size", "_it")
    1403
    1404    def __init__(self, it: Iterator[Item], chunk_size: int) -> None:
    1405        self.chunk_size = chunk_size
    1406        self._it = it
    1407
    1408    def __next__(self) -> collections.Sequence[Item]:
    1409        chunk: list[Item] = []
    1410
    1411        for item in self._it:
    1412            chunk.append(item)
    1413
    1414            if len(chunk) == self.chunk_size:
    1415                break
    1416
    1417        if chunk:
    1418            return chunk
    1419
    1420        unreachable()

An iterator that yields elements in chunks.

This iterator is created by the `Iterator.chunks` method.

Chunks(it: Iterator[~Item], chunk_size: int) View Source

    1404    def __init__(self, it: Iterator[Item], chunk_size: int) -> None:
    1405        self.chunk_size = chunk_size
    1406        self._it = it

chunk_size

##### Inherited Members

Iterator
  default
  collect
  collect_into
  to_vec
  sink
  raw_parts
  next
  cloned
  copied
  map
  filter
  take
  skip
  enumerate
  take_while
  drop_while
  chunks
  all
  any
  zip
  sort
  reversed
  union
  first
  last
  count
  find
  position
  fold
  advance_by
  nth
  sum
  for_each
  async_for_each

@typing.final
@diagnostic
class Empty(typing.Generic[~Item], sain.iter.ExactSizeIterator[~Item]): View Source

    1423@typing.final
    1424@diagnostic
    1425class Empty(typing.Generic[Item], ExactSizeIterator[Item]):
    1426    """An iterator that yields nothing.
    1427
    1428    This is the default iterator that is created by `Iterator.default()` or `empty()`
    1429    """
    1430
    1431    __slots__ = ()
    1432
    1433    def __init__(self) -> None:
    1434        pass
    1435
    1436    def next(self) -> Option[Item]:
    1437        # SAFETY: an empty iterator always returns None.
    1438        # also we avoid calling `nothing_unchecked()` here for fast returns.
    1439        return _option.NOTHING
    1440
    1441    def __len__(self) -> typing.Literal[0]:
    1442        return 0
    1443
    1444    def any(
    1445        self, predicate: collections.Callable[[Item], bool]
    1446    ) -> typing.Literal[False]:
    1447        return False
    1448
    1449    def all(
    1450        self, predicate: collections.Callable[[Item], bool]
    1451    ) -> typing.Literal[False]:
    1452        return False
    1453
    1454    def __next__(self) -> Item:
    1455        unreachable()

An iterator that yields nothing.

This is the default iterator that is created by `Iterator.default()` or `empty()`

def next(self) -> 'Option[Item]': View Source

    1436    def next(self) -> Option[Item]:
    1437        # SAFETY: an empty iterator always returns None.
    1438        # also we avoid calling `nothing_unchecked()` here for fast returns.
    1439        return _option.NOTHING

Advance the iterator, Returning the next item, `Some(None)` if all items yielded.

###### Example

    iterator = Iter(["1", "2"])
    assert iterator.next() == Some("1")
    assert iterator.next() == Some("2")
    assert iterator.next().is_none()

def any(self, predicate: Callable[[~Item], bool]) -> Literal[False]: View Source

    1444    def any(
    1445        self, predicate: collections.Callable[[Item], bool]
    1446    ) -> typing.Literal[False]:
    1447        return False

`True` if any items in the iterator match the predicate.

###### Example

    iterator = Iter([1, 2, 3])
    if iterator.any(lambda item: isinstance(item, int)):
        print("At least one item is an int.")
    # At least one item is an int.

###### Parameters

- predicate (`collections.Callable[[Item], bool]`): The function to test each item in the iterator.

def all(self, predicate: Callable[[~Item], bool]) -> Literal[False]: View Source

    1449    def all(
    1450        self, predicate: collections.Callable[[Item], bool]
    1451    ) -> typing.Literal[False]:
    1452        return False

Return `True` if all items in the iterator match the predicate.

###### Example

    iterator = Iter([1, 2, 3])
    while iterator.all(lambda item: isinstance(item, int)):
        print("Still all integers")
        continue
        # Still all integers

###### Parameters

- predicate (`collections.Callable[[Item], bool]`): The function to test each item in the iterator.

##### Inherited Members

ExactSizeIterator
  count
  len
  is_empty
Iterator
  default
  collect
  collect_into
  to_vec
  sink
  raw_parts
  cloned
  copied
  map
  filter
  take
  skip
  enumerate
  take_while
  drop_while
  chunks
  zip
  sort
  reversed
  union
  first
  last
  find
  position
  fold
  advance_by
  nth
  sum
  for_each
  async_for_each

@typing.final
@diagnostic
class Repeat(typing.Generic[~Item], sain.iter.ExactSizeIterator[~Item]): View Source

    1458@typing.final
    1459@diagnostic
    1460class Repeat(typing.Generic[Item], ExactSizeIterator[Item]):
    1461    """An iterator that repeats a given value an exact number of times.
    1462
    1463    This iterator is created by calling `repeat()`.
    1464    """
    1465
    1466    __slots__ = ("_count", "_element")
    1467
    1468    def __init__(self, element: Item, count: int) -> None:
    1469        self._count = count
    1470        self._element = element
    1471
    1472    def __next__(self) -> Item:
    1473        if self._count > 0:
    1474            self._count -= 1
    1475            if self._count == 0:
    1476                # Return the origin element last
    1477                return self._element
    1478
    1479            return copy.copy(self._element)
    1480
    1481        unreachable()
    1482
    1483    def __len__(self) -> int:
    1484        return self._count

An iterator that repeats a given value an exact number of times.

This iterator is created by calling `repeat()`.

Repeat(element: ~Item, count: int) View Source

    1468    def __init__(self, element: Item, count: int) -> None:
    1469        self._count = count
    1470        self._element = element

##### Inherited Members

ExactSizeIterator
  count
  len
  is_empty
Iterator
  default
  collect
  collect_into
  to_vec
  sink
  raw_parts
  next
  cloned
  copied
  map
  filter
  take
  skip
  enumerate
  take_while
  drop_while
  chunks
  all
  any
  zip
  sort
  reversed
  union
  first
  last
  find
  position
  fold
  advance_by
  nth
  sum
  for_each
  async_for_each

@typing.final
@diagnostic
class Once(typing.Generic[~Item], sain.iter.ExactSizeIterator[~Item]): View Source

    1487@typing.final
    1488@diagnostic
    1489class Once(typing.Generic[Item], ExactSizeIterator[Item]):
    1490    """An iterator that yields exactly one item.
    1491
    1492    This iterator is created by calling `once()`.
    1493    """
    1494
    1495    __slots__ = ("_item",)
    1496
    1497    def __init__(self, item: Item) -> None:
    1498        self._item: Item | None = item
    1499
    1500    def __next__(self) -> Item:
    1501        if self._item is None:
    1502            unreachable()
    1503
    1504        i = self._item
    1505        self._item = None
    1506        return i
    1507
    1508    def __len__(self) -> int:
    1509        return 1 if self._item is not None else 0

An iterator that yields exactly one item.

This iterator is created by calling `once()`.

Once(item: ~Item) View Source

    1497    def __init__(self, item: Item) -> None:
    1498        self._item: Item | None = item

##### Inherited Members

ExactSizeIterator
  count
  len
  is_empty
Iterator
  default
  collect
  collect_into
  to_vec
  sink
  raw_parts
  next
  cloned
  copied
  map
  filter
  take
  skip
  enumerate
  take_while
  drop_while
  chunks
  all
  any
  zip
  sort
  reversed
  union
  first
  last
  find
  position
  fold
  advance_by
  nth
  sum
  for_each
  async_for_each

class ExactSizeIterator(typing.Generic[~Item], sain.iter.Iterator[~Item], abc.ABC): View Source

    930class ExactSizeIterator(typing.Generic[Item], Iterator[Item], abc.ABC):
    931    """An iterator that knows its exact size.
    932
    933    The implementations of this interface indicates that the iterator knows exactly
    934    how many items it can yield.
    935
    936    however, this is not a requirement for the iterator to implement this trait, as its
    937    only used for iterators that can know their size.
    938
    939    Example
    940    -------
    941    ```py
    942    @dataclass
    943    class Letters(ExactSizeIterator[str]):
    944        letters: list[str]
    945
    946        def __next__(self) -> str:
    947            return self.letters.pop(0)
    948
    949        def __len__(self) -> int:
    950            return len(self.letters)
    951
    952    letters = Letters(['a', 'b', 'c'])
    953    assert letters.count() == 3
    954    assert letters.next() == Some('a')
    955    assert letters.count() == 2
    956    ```
    957    """
    958
    959    __slots__ = ()
    960
    961    @typing.final
    962    def count(self) -> int:
    963        return len(self)
    964
    965    @typing.final
    966    def len(self) -> int:
    967        """Returns the remaining number of items in the iterator.
    968
    969        This doesn't exhaust the iterator.
    970
    971        Example
    972        -------
    973        ```py
    974        it = once(0)
    975        assert it.len() == 1
    976        assert it.len() == 1
    977        it.next()
    978        assert it.len() == 0
    979        ```
    980        """
    981        return len(self)
    982
    983    @typing.final
    984    def is_empty(self) -> bool:
    985        """Return `True` if this iterator has no items left to yield.
    986
    987        Example
    988        -------
    989        ```py
    990        iterator = once(1)
    991        assert not iterator.is_empty()
    992        assert once.next() == Some(1)
    993        assert iterator.is_empty()
    994        ```
    995        """
    996        return len(self) == 0
    997
    998    @abc.abstractmethod
    999    def __len__(self) -> int: ...

An iterator that knows its exact size.

The implementations of this interface indicates that the iterator knows exactly how many items it can yield.

however, this is not a requirement for the iterator to implement this trait, as its only used for iterators that can know their size.

###### Example

    @dataclass
    class Letters(ExactSizeIterator[str]):
        letters: list[str]

        def __next__(self) -> str:
            return self.letters.pop(0)

        def __len__(self) -> int:
            return len(self.letters)

    letters = Letters(['a', 'b', 'c'])
    assert letters.count() == 3
    assert letters.next() == Some('a')
    assert letters.count() == 2

@typing.final
def count(self) -> int: View Source

    961    @typing.final
    962    def count(self) -> int:
    963        return len(self)

Return the count of elements in memory this iterator has.

###### Example

    it = Iter(range(3))
    assert it.count() == 3

@typing.final
def len(self) -> int: View Source

    965    @typing.final
    966    def len(self) -> int:
    967        """Returns the remaining number of items in the iterator.
    968
    969        This doesn't exhaust the iterator.
    970
    971        Example
    972        -------
    973        ```py
    974        it = once(0)
    975        assert it.len() == 1
    976        assert it.len() == 1
    977        it.next()
    978        assert it.len() == 0
    979        ```
    980        """
    981        return len(self)

Returns the remaining number of items in the iterator.

This doesn't exhaust the iterator.

###### Example

    it = once(0)
    assert it.len() == 1
    assert it.len() == 1
    it.next()
    assert it.len() == 0

@typing.final
def is_empty(self) -> bool: View Source

    983    @typing.final
    984    def is_empty(self) -> bool:
    985        """Return `True` if this iterator has no items left to yield.
    986
    987        Example
    988        -------
    989        ```py
    990        iterator = once(1)
    991        assert not iterator.is_empty()
    992        assert once.next() == Some(1)
    993        assert iterator.is_empty()
    994        ```
    995        """
    996        return len(self) == 0

Return `True` if this iterator has no items left to yield.

###### Example

    iterator = once(1)
    assert not iterator.is_empty()
    assert once.next() == Some(1)
    assert iterator.is_empty()

##### Inherited Members

Iterator
  default
  collect
  collect_into
  to_vec
  sink
  raw_parts
  next
  cloned
  copied
  map
  filter
  take
  skip
  enumerate
  take_while
  drop_while
  chunks
  all
  any
  zip
  sort
  reversed
  union
  first
  last
  find
  position
  fold
  advance_by
  nth
  sum
  for_each
  async_for_each

@rustc_diagnostic_item('into_iter')
def into_iter( iterable: Sequence[~Item] | Iterable[~Item]) -> Union[Iter[~Item], TrustedIter[~Item], TrustedIter[int]]: View Source

    1580@rustc_diagnostic_item("into_iter")
    1581def into_iter(
    1582    iterable: collections.Sequence[Item] | collections.Iterable[Item],
    1583) -> Iter[Item] | TrustedIter[Item] | TrustedIter[int]:
    1584    """Convert any iterable into `Iterator[Item]`.
    1585
    1586    if the size of the iterable is known, it will return `TrustedIter`,
    1587    otherwise it will return `Iter`.
    1588
    1589    Example
    1590    -------
    1591    ```py
    1592    sequence = [1,2,3]
    1593    for item in sain.into_iter(sequence).reversed():
    1594        print(item)
    1595    # 3
    1596    # 2
    1597    # 1
    1598    ```
    1599    """
    1600    if isinstance(iterable, collections.Sequence):
    1601        return TrustedIter(iterable)
    1602    return Iter(iterable)

Convert any iterable into `Iterator[Item]`.

if the size of the iterable is known, it will return `TrustedIter`, otherwise it will return `Iter`.

###### Example

    sequence = [1,2,3]
    for item in sain.into_iter(sequence).reversed():
        print(item)
    # 3
    # 2
    # 1

# Implementations

This function implements .into_iter">into_iter in Rust.

@rustc_diagnostic_item('empty')
def empty() -> Empty[~Item]: View Source

    1513@rustc_diagnostic_item("empty")
    1514def empty() -> Empty[Item]:  # pyright: ignore
    1515    """Create an iterator that yields nothing.
    1516
    1517    Example
    1518    -------
    1519    ```py
    1520    nope: Iterator[int] = sain.iter.empty()
    1521    assert nope.next().is_none()
    1522    ```
    1523    """
    1524    return Empty()

Create an iterator that yields nothing.

###### Example

    nope: Iterator[int] = sain.iter.empty()
    assert nope.next().is_none()

# Implementations

This function implements empty in Rust.

@rustc_diagnostic_item('once')
def once(item: ~Item) -> Once[~Item]: View Source

    1552@rustc_diagnostic_item("once")
    1553def once(item: Item) -> Once[Item]:
    1554    """Returns an iterator that yields exactly a single item.
    1555
    1556    Example
    1557    -------
    1558    ```py
    1559    iterator = sain.iter.once(1)
    1560    assert iterator.next() == Some(1)
    1561    assert iterator.next() == Some(None)
    1562    ```
    1563    """
    1564    return Once(item)

Returns an iterator that yields exactly a single item.

###### Example

    iterator = sain.iter.once(1)
    assert iterator.next() == Some(1)
    assert iterator.next() == Some(None)

# Implementations

This function implements once in Rust.

@rustc_diagnostic_item('repeat')
def repeat(element: ~Item, count: int) -> Repeat[~Item]: View Source

    1527@rustc_diagnostic_item("repeat")
    1528def repeat(element: Item, count: int) -> Repeat[Item]:
    1529    """Returns an iterator that yields the exact same `element` number of `count` times.
    1530
    1531    The yielded elements is a copy of `element`, but the last element is guaranteed to be the same as the
    1532    original `element`.
    1533
    1534    Example
    1535    -------
    1536    ```py
    1537    nums = [1, 2, 3]
    1538    it = sain.iter.repeat(nums, 5)
    1539    for i in range(4):
    1540        cloned = it.next().unwrap()
    1541        assert cloned == [1, 2, 3]
    1542
    1543    # But the last item is the origin one...
    1544    last = it.next().unwrap()
    1545    last.append(4)
    1546    assert nums == [1, 2, 3, 4]
    1547    ```
    1548    """
    1549    return Repeat(element, count)

Returns an iterator that yields the exact same `element` number of `count` times.

The yielded elements is a copy of `element`, but the last element is guaranteed to be the same as the original `element`.

###### Example

    nums = [1, 2, 3]
    it = sain.iter.repeat(nums, 5)
    for i in range(4):
        cloned = it.next().unwrap()
        assert cloned == [1, 2, 3]

    # But the last item is the origin one...
    last = it.next().unwrap()
    last.append(4)
    assert nums == [1, 2, 3, 4]

# Implementations

This function implements repeat in Rust.