Help creating a trait to make Iterators

I'm still kind of new to Rust, trying to wrap my head around parts of it. As an exercise I tried to make it possible to create iterators like this (in this case for the Fibonacci sequence):

(1,1).iterize(|p| (p.1, p.0 + p.1))

You specify a tuple with the first two values, and a function that takes that and produces the tuple for the next iteration. The Iterator just calls the function repeatedly and returns the old tuple.0 values.

Seems reasonably straightforward, but I can't get it to work. The main problem is

error[E0562]: `impl Trait` not allowed outside of function and inherent method return types
  --> src/main.rs:31:36
   |
31 |     fn iterize(&mut self, f: F) -> impl Iterator<Item = R>;  // E0562
   |                                    ^^^^^^^^^^^^^^^^^^^^^^^

Does that mean that trait methods are not allowed to return impl traits? If so, is there another efficient (I assume using dyn would drastically impact performance) way to do what I am trying to do?

You need to let the iterator be an associated type on the trait:

trait Iterizer<R,S,F,I>
where F: Fn((R,S)) -> (R,S),
      I: Iterator<Item = R>
{
    type Iter: Iterator<Item = R>;
    fn iterize(&mut self, f: F) -> Self::Iter;
}

Note that this means you must be able to write the type down, so you cannot use iterators that have a closure as a generic parameter, as the type of the closure cannot be named.

Thanks, Alice -- I mostly understood that. :slight_smile:

I came to the conclusion that this wasn't going to work with the API I had imagined, so I made iterize() a plain function, and that works:

fn main() {
    dbg!(fib().take(6).collect::<Vec<i32>>());
}

// Returns an Iterator for the Fibonacci sequence: 1 1 2 3 5 8 ...
fn fib() -> impl Iterator<Item = i32> {
    iterize((1,1), |p| (p.1, p.0 + p.1))
}

// Given an initial state of type (R,S) and a function that produces a new state
// from an old state, return an Iterator for the Rs.
fn iterize<R: Copy, S: Copy>(s0: (R,S), f: impl Fn((R,S)) -> (R,S)) -> impl Iterator<Item = R> {
    let mut state = s0;
    std::iter::repeat_with(
        move || { state.swap(f(state)).0 }
    )
}

// a.swap(b) sets a to b and returns the old value of a.
pub trait Swap: Sized {
    fn swap(&mut self, value: Self) -> Self;
}
impl<T> Swap for T {
    // Sets self to the new value and returns the old value.
    fn swap(&mut self, new: Self) -> Self {
        std::mem::replace(self, new)
    }
} 

OK, so

  1. Any suggestions? I'm new at this.
  2. Is there a canonical facility for doing this?

I would probably simply go for Clone instead of Copy. That way the user can use it with types that are only clone.

1 Like

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