Pre-RFC: [Ergonomics] Iterator from FnMut() -> Option<Item>

  • Feature Name: iter_from_closure
  • Start Date: 2017-09-03
  • RFC PR: (leave this empty)
  • Rust Issue: (leave this empty)

Summary

Add a function iter_from_closure for quickly transforming a mutable closure of the right form into an Iterator.

In other words, the semantics are:

fn iter_from_closure<Item, F>(closure: F) -> impl Iterator<Item = Item>
where F: FnMut() -> Option<Item>

Motivation

Sometimes, you have some state, and from that, you need to create an Iterator, but only once - and never for reuse. In such circumstances, it is not ergonomic to create a new struct just to hold the relevant state and then impl Iterator for that struct and then instantiate the struct.

A more ergonomic solution is to simply take any state you already have, capture it in an FnMut() -> Option<Item> and then let that closure implement Iterator<Item = Item>. Unfortunately, it is not possible for FnMut() -> Option<Item> to directly implement Iterator<Item = Item>.

Detailed design

Create a function iter_from_closure which returns an object implementing Iterator<Item = Item>:

// [bikeshed] iter_from_closure
fn iter_from_closure<Item, F>(closure: F) -> MutFn<Item, F>
where
    F: FnMut() -> Option<Item>
{
    MutFn::new(closure)
}

where:

// [bikeshed] MutFn
struct MutFn<Item, F>
where
    F: FnMut() -> Option<Item>
{
    closure: F
}

impl<Item, F> MutFn<Item, F>
where
    F: FnMut() -> Option<Item>
{
    fn new(closure: F) -> Self {
        MutFn {
            closure
        }
    }
}

impl<Item, F> Iterator for MutFn<Item, F>
where
    F: FnMut() -> Option<Item>
{
    type Item = Item;

    fn next(&mut self) -> Option<Self::Item> {
        (self.closure)()
    }
}

The function iter_from_closure can then be used as in:

fn main() {
    let mut count = 10;
    let iter = iter_from_closure(|| {
        let c = count;
        count = c - 1;
        if c > 0 { Some(c) } else { None }
    });

    println!("{:?}", iter.collect::<Vec<_>>());
}

The function MutFn::new is not meant to be stabilized at this time. Instead, the public API is iter_from_closure.

How We Teach This

Documentation on iter_from_closure and possibly MutFn should be enough.

Drawbacks

This could be done in an external crate - it could be considered bloat in core.

Alternatives

  • Implement this in an external crate.

Unresolved questions

  • Should impl Iterator<Item = Item> be used instead? Currently, conservative_impl_trait will take much longer to stabilize, so will iter_from_closure take as long to stabilize?
  • Is this needed given the work on generators / co-routines?
1 Like

... and this alternative has already been implemented.

extern crate itertools;

use itertools::{repeat_call, Itertools};

fn main() {
    let mut count = 10;
    let iter = repeat_call(|| {
        let c = count;
        count = c - 1;
        if c > 0 { Some(c) } else { None }
    }).while_some();
    
    println!("{:?}", iter.collect::<Vec<_>>());
}
4 Likes

Oh - blimey, I guess I was a bit sloppy when searching in itertools.

Still, this is not exactly the same and does not convey intent well. Having to add .while_some() to remove the needless extra Option layer is a bit weird, optimizations notwithstanding.

1 Like

“Closing” this Pre-RFC for now - unless someone really wants it - in favour of itertools::unfold which is a slightly more general version of what this Pre-RFC proposes.

2 Likes

Potentially yield in any function will do this if coroutines are in the language.

2 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.