There’s multiple options with trade-off between convenience/performance here. The optimal solution would probably be to use type_alias_impl_trait
s but that’s not a stable feature of Rust yet. Btw. the double closure thing doesn’t make sense and isn’t necessary. With that unstable feature it would look like
#![feature(type_alias_impl_trait)]
#[derive(Default)]
struct Counter {
count: u32,
}
impl IntoIterator for Counter {
type Item = u32;
type IntoIter = std::iter::FromFn<impl FnMut() -> Option<Self::Item>>;
fn into_iter(mut self) -> Self::IntoIter {
std::iter::from_fn(move || {
self.count += 1;
Some(self.count)
})
}
}
fn main() {
eprintln!(
"{:?}",
Counter::default().into_iter().take(5).collect::<Vec<_>>()
)
}
Rust Playground
or
#![feature(type_alias_impl_trait)]
#[derive(Default)]
struct Counter {
count: u32,
}
impl IntoIterator for Counter {
type Item = u32;
type IntoIter = impl Iterator<Item = Self::Item>;
fn into_iter(mut self) -> Self::IntoIter {
std::iter::from_fn(move || {
self.count += 1;
Some(self.count)
})
}
}
fn main() {
eprintln!(
"{:?}",
Counter::default().into_iter().take(5).collect::<Vec<_>>()
)
}
Rust Playground
On stable Rust, you can do a similar approach (but using dynamic function calls) by using trait objects instead, and you’ll have to box them:
#[derive(Default)]
struct Counter {
count: u32,
}
impl IntoIterator for Counter {
type Item = u32;
type IntoIter = std::iter::FromFn<Box<dyn FnMut() -> Option<Self::Item>>>;
fn into_iter(mut self) -> Self::IntoIter {
std::iter::from_fn(Box::new(move || {
self.count += 1;
Some(self.count)
}))
}
}
fn main() {
eprintln!(
"{:?}",
Counter::default().into_iter().take(5).collect::<Vec<_>>()
)
}
Rust Playground
or
#[derive(Default)]
struct Counter {
count: u32,
}
impl IntoIterator for Counter {
type Item = u32;
type IntoIter = Box<dyn Iterator<Item = Self::Item>>;
fn into_iter(mut self) -> Self::IntoIter {
Box::new(std::iter::from_fn(move || {
self.count += 1;
Some(self.count)
}))
}
}
fn main() {
eprintln!(
"{:?}",
Counter::default().into_iter().take(5).collect::<Vec<_>>()
)
}
Rust Playground
Regarding the question whether the Iterator
or FnMut
is the right abstraction level, I suppose I’d prefer the Iterator
(for either the unstable impl Iterator<…>
or the stable Box<dyn Iterator<…>
approach. Depending on the use case, for the trait object it might also make sense (or even be important/necesary) to include some auto-trait bounds, so something like Box<dyn Iterator<Item = Self::Item> + Send + Sync>
.
Now, the alternative (and slightly more verbose) approach is to work with a manual Iterator
implementation instead. You’d define a struct that captures the necessary state, instead of the closure, and you’d implement Iterator
for that. This avoids any dynamic function calls, so it’s more performant; the main downside is that it might be somewhat more verbose to type, but decide for yourself.
#[derive(Default)]
struct Counter {
count: u32,
}
impl IntoIterator for Counter {
type Item = u32;
type IntoIter = IntoIter;
fn into_iter(self) -> Self::IntoIter {
IntoIter(self)
}
}
// Naming-wise, if this whole thing lives in a module called `counter`, a generic
// "IntoIter" name makes sense, similar to how many standard library collections do it.
struct IntoIter(Counter);
impl Iterator for IntoIter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
self.0.count += 1;
Some(self.0.count)
}
}
fn main() {
eprintln!(
"{:?}",
Counter::default().into_iter().take(5).collect::<Vec<_>>()
)
}
Rust Playground
For this particular use-case of an infinite iterator note that you could use repeat_with in place of from_fn, too, which does e.g. give a more precise size_hint
implementation. For the manual Iterator
you could, similarly, implement size_hint
accordingly, in this example returning (usize::MAX, None)
.