iterate_trait/
lib.rs

1//! Experiment with methods on IntoIterator
2//!
3//! ## Why does this project exist?
4//!
5//! This project asks the question: what if we used `IntoIterator` everywhere
6//! instead of `Iterator`? This becomes relevant for generator blocks, which
7//! themselves may contain `!Send` items, but that doesn't mean that the type
8//! returned by `gen {}` needs to be `!Send` too. This crate follows Swift's
9//! example, making it so all operations happen on a base builder type - which
10//! has one final operation that converts it into an actual iterable.
11//!
12//! The other reason is that in bounds we already always use `IntoIterator`. For
13//! example the `collect` method takes `A: IntoIterator`, not `A: Iterator`. In
14//! function bounds there is rarely a reason to use `Iterator` directly; typically the
15//! only reason we don't is because it's more effort to type.
16//!
17//! ## Example of `Iterator`'s limitations
18//!
19//! Here's a practical case people are bound to hit when writing generator
20//! blocks, which can't be fixed unless generator returns `IntoIterator`:
21//!
22//! ```rust
23//! // A gen block that holds some `!Send` type across a yield point
24//! let iter = gen {
25//!     let items = my_data.lock(); // ← `MutexGuard: !Send`
26//!     for item in items {
27//!         yield item;
28//!     }
29//! };
30//!
31//! // ## Option 1
32//! let iter = gen { ... };      // 1. Desugars to `impl Iterator + !Send`
33//! thread::spawn(move || {      // 2. ❌ Can't move `!Send` type across threads
34//!     for item in iter { ... }
35//! }).unwrap();
36//!
37//! // ## Option 2
38//! let iter = gen { ... };      // 1. Desugars to `impl IntoIterator + Send`
39//! thread::spawn(move || {      // 2. ✅ Move `Send` type across threads
40//!     for item in iter { ... } // 3. Obtain `impl Iterator + !Send`
41//! }).unwrap();
42//! ```
43//!
44//! ## Why did you choose these names?
45//!
46//! This crate essentially reframes `IntoIterator` into the main interface for
47//! iteration. However the name `IntoIterator` suggests it is a mere supplement
48//! to some other `Iterator` trait. `Iterator` also has another quirk: it's a
49//! trait that's named after a noun, rather than a verb. Think of `Read`,
50//! `Write`, `Send` - these are all verbs.
51//!
52//! The closest prior art for this in the stdlib I could find was the `Hash` /
53//! `Hasher` pair. The main trait `Hash` is the subject of the hashing, with
54//! `Hasher` containing all the hash state. This makes `Hasher` very similar to
55//! `Iterator`, and hints the better name for `IntoIterator` might in fact be `Iterate`.
56//!
57//! This just leaves us with what to do about `FromIterator`, which currently
58//! exists as a dual to `IntoIterator`. But interestingly it also exists as a
59//! dual to [`Extend`](https://doc.rust-lang.org/std/iter/trait.Extend.html),
60//! where rather than creating a new container it can be used to extend an
61//! existing collection. This is also used in the unstable [`collect_into`
62//! method](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect_into).
63//! It's for this reason that we've renamed `FromIterator` to `Collect`. All
64//! together this changes the names to:
65//!
66//! - `IntoIterator` → `Iterate`
67//! - `Iterator` → `Iterator`
68//! - `FromIterator` → `Collect`
69
70#![forbid(unsafe_code, rust_2018_idioms)]
71#![deny(missing_debug_implementations, nonstandard_style)]
72#![warn(missing_docs, future_incompatible, unreachable_pub)]
73
74pub mod map;
75
76/// A stateful iterator returned by [`Iterate::iterate`].
77pub trait Iterator {
78    /// The type of the elements being iterated over.
79    type Item;
80
81    /// Advances the iterator and returns the next value.
82    fn next(&mut self) -> Option<Self::Item>;
83
84    /// How many items do we expect to yield?
85    fn size_hint(&self) -> (usize, Option<usize>) {
86        (0, None)
87    }
88}
89
90/// Provide sequential, iterated access to items.
91pub trait Iterate {
92    /// The type of the elements being iterated over.
93    type Item;
94
95    /// Which kind of iterator are we turning this into?
96    type Iterator: Iterator<Item = Self::Item>;
97
98    /// Begin iteration and obtain a stateful [`Iterator`].
99    fn iterate(self) -> Self::Iterator;
100
101    /// Maps the values of iter with f.
102    fn map<F, B>(self, f: F) -> map::IntoMap<Self, F>
103    where
104        F: FnOnce(Self::Item) -> B,
105        Self: Sized,
106    {
107        map::IntoMap::new(self, f)
108    }
109
110    /// Transforms this iterator into a collection.
111    fn collect<B: Collect<Self::Item>>(self) -> B
112    where
113        Self: Sized,
114    {
115        Collect::collect(self)
116    }
117}
118
119impl<T> Iterate for T
120where
121    T: Iterator,
122{
123    type Item = T::Item;
124
125    type Iterator = T;
126
127    fn iterate(self) -> Self::Iterator {
128        self
129    }
130}
131
132/// Iterate over items and collect them into a value.
133pub trait Collect<A>: Sized {
134    /// Creates a value from an `Iterate`.
135    fn collect<T: Iterate<Item = A>>(iter: T) -> Self;
136}