Here's a first attempt. I had to remove the lifetime annotations from the Streamer trait definition as it was causing lifetimes to be needed everywhere, and the compiler seems to do better without it:
pub trait Streamer {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
// A source is a struct of 'closure' variables for the generator plus the implementation of the Streamer trait
struct StreamSource {
counter : i32
}
impl Streamer for StreamSource {
type Item = i32;
fn next(&mut self) -> Option<Self::Item> {
print!("[generate] ");
let t = self.counter;
self.counter += 1;
Some(t)
}
}
// A sink is just a normal generic function that takes a Streamer
fn stream_sink<S>(mut s : S) where S : Streamer, S::Item : std::fmt::Display {
for _ in 0..10 {
match s.next() {
Some(x) => println!("{}", x),
None => break
}
}
}
fn main() {
// A filter is a struct for 'closure' variables and an implementation of Streamer
// with a type parameter for the type of the source streamer and a value "input"
// for the value of the actual streamer passed.
struct StreamAdd<S> where S : Streamer {
input : S,
add : S::Item
}
impl<S> Streamer for StreamAdd<S> where S : Streamer, <S as Streamer>::Item : std::ops::Add<Output = S::Item> + Clone {
type Item = S::Item;
fn next(&mut self) -> Option<Self::Item> {
print!("[filter] ");
match self.input.next() {
Some(x) => Some(x + self.add.clone()),
None => None
}
}
}
stream_sink(StreamAdd{input: StreamSource{counter : 0}, add : 10});
}
Leaving out the plumbing of the Streamer source and sink, the important part is the definition of a filter:
struct StreamAdd<S> where S : Streamer {
input : S
add : S::Item
}
impl<S> Streamer for StreamAdd<S> where S : Streamer, <S as Streamer>::Item : std::ops::Add<Output = S::Item> + Clone {
type Item = S::Item;
fn next(&mut self) -> Option<Self::Item> {
print!("[filter] ");
match self.input.next() {
Some(x) => Some(x + self.add.clone()),
None => None
}
}
}
Personally I think its fairly simple to understand, StreamAdd is a record with fields for the input and any local data needed by the filter. The filter itself is an implementation of Streamer that has another streamer as its type parameter, but this can be inferred from the value put in 'input'. If you read the final line the filter reads somewhat like a function with named parameters. I also think there is no difference between a Streamer and a normal Iterator, the 'concept' is the same it is the algorithms that are different. So an Iterator is a Streamer, but an eager_filter is a different algorithm than a lazy_filter.