Is there a function in Rust equivalent to Java's Stream.Iterate?
As of Rust 1.7, there's nothing in Rust's standard library that does the equivalent of Stream.iterate
(or I couldn't find it!).
I just whipped up the following implementation in Rust. It's not as straightforward as the Java implementation might be, because we have to take care of ownership (hence the requirement for Clone
and the Option
dance with the value
field).
struct SequenceGenerator<T, F> {
value: Option<T>,
calc_next: F,
}
impl<T, F> SequenceGenerator<T, F>
where T: Clone,
F: FnMut(T) -> T
{
fn new(value: T, calc_next: F) -> SequenceGenerator<T, F> {
SequenceGenerator {
value: Some(value),
calc_next: calc_next,
}
}
}
impl<T, F> Iterator for SequenceGenerator<T, F>
where T: Clone,
F: FnMut(T) -> T
{
type Item = T;
fn next(&mut self) -> Option<T> {
let result = self.value.as_ref().unwrap().clone();
self.value = Some((self.calc_next)(self.value.take().unwrap()));
Some(result)
}
}
fn main() {
let seq_gen = SequenceGenerator::new(1, |x| 2 * x);
for i in seq_gen.take(10) {
println!("{}", i);
}
}
The Rust standard library used to have similar functionality under the name unfold
, but it was never made stable and was eventually removed. It now lives in the itertools crate:
extern crate itertools;
use itertools::Unfold;
fn main() {
let x = Unfold::new(1, |x| {
*x *= 2;
Some(*x)
});
for val in x.take(10) {
println!("{}", val);
}
}
Note that it's a bit more complicated because the state doesn't have to exactly match with what the iterator returns and you can control when the iterator stops. It's possible that the crate would accept a PR for a thin layer on top that gives your exact implementation.
You can use standard scan
iterator:
let seq_gen = iter::repeat(())
.scan(1, |r,_|{
let out = *r; *r = out * 2 ; Some(out)
});
or with explicit closure definition:
let seq_gen = iter::repeat(())
.scan((1, |x| x*2), |r,_|{
let out = r.0; r.0 = r.1(r.0); Some(out)
});
For noncopyable types things looks worse:
let seq_gen = iter::repeat(())
.scan(Some("Hello world".to_owned()), |r,_|{
let out = r.clone(); *r = r.take().map(|x| x+"!") ; out
});
For these types it is better to use functions that modify the value in place:
let seq_gen = iter::repeat(())
.scan("Hello world".to_owned(), |r,_|{
let out = r.clone(); r.push_str("!") ; Some(out)
});