iterate-trait

Crates.ioiterate-trait
lib.rsiterate-trait
version1.0.1
sourcesrc
created_at2024-11-25 12:35:40.748983
updated_at2024-11-25 14:18:20.162548
descriptionExperiment with methods on IntoIterator
homepage
repositoryhttps://github.com/yoshuawuyts/iterate-trait
max_upload_size
id1460275
size31,414
Yosh (yoshuawuyts)

documentation

https://docs.rs/iterate-trait

README

iterate-trait

Experiment with methods on IntoIterator

Crates.io version Download docs.rs docs

API Docs | Releases | Contributing

Why does this project exist?

This project asks the question: what if we used IntoIterator everywhere instead of Iterator? This becomes relevant for generator blocks, which themselves may contain !Send items, but that doesn't mean that the type returned by gen {} needs to be !Send too. This crate follows Swift's example, making it so all operations happen on a base builder type - which has one final operation that converts it into an actual iterable.

The other reason is that in bounds we already always use IntoIterator. For example the collect method takes A: IntoIterator, not A: Iterator. In function bounds there is rarely a reason to use Iterator directly; typically the only reason we don't is because it's more effort to type.

Example of Iterator's limitations

Here's a practical case people are bound to hit when writing generator blocks, which can't be fixed unless generator returns IntoIterator:

// A gen block that holds some `!Send` type across a yield point
let iter = gen {
    let items = my_data.lock(); // ← `MutexGuard: !Send`
    for item in items {
        yield item;
    }
};

// ## Option 1
let iter = gen { ... };      // 1. Desugars to `impl Iterator + !Send`
thread::spawn(move || {      // 2. ❌ Can't move `!Send` type across threads
    for item in iter { ... }
}).unwrap();

// ## Option 2
let iter = gen { ... };      // 1. Desugars to `impl IntoIterator + Send`
thread::spawn(move || {      // 2. ✅ Move `Send` type across threads
    for item in iter { ... } // 3. Obtain `impl Iterator + !Send`
}).unwrap();

Why did you choose these names?

This crate essentially reframes IntoIterator into the main interface for iteration. However the name IntoIterator suggests it is a mere supplement to some other Iterator trait. Iterator also has another quirk: it's a trait that's named after a noun, rather than a verb. Think of Read, Write, Send - these are all verbs.

The closest prior art for this in the stdlib I could find was the Hash / Hasher pair. The main trait Hash is the subject of the hashing, with Hasher containing all the hash state. This makes Hasher very similar to Iterator, and hints the better name for IntoIterator might in fact be Iterate.

This just leaves us with what to do about FromIterator, which currently exists as a dual to IntoIterator. But interestingly it also exists as a dual to Extend, where rather than creating a new container it can be used to extend an existing collection. This is also used in the unstable collect_into method. It's for this reason that we've renamed FromIterator to Collect. All together this changes the names to:

  • IntoIteratorIterate
  • IteratorIterator
  • FromIteratorCollect

Installation

$ cargo add iterate-trait

Safety

This crate uses #![deny(unsafe_code)] to ensure everything is implemented in 100% Safe Rust.

Contributing

Want to join us? Check out our "Contributing" guide and take a look at some of these issues:

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Commit count: 12

cargo fmt