I'm trying to see if I can create function that takes one iterator type and transforms it into another, while preserving the functionality that Iterator types like Map have:
fn cust_chunk<'a, I>(iter: I, n: usize) -> impl Iterator<Item = String>
where I: Iterator<Item= &'a char>,
{
let res = iter
.take(n)
.fold(String::new(), |acc, c| acc + &c.to_string());
let res = if res == "" {None} else {Some(res)};
res.into_iter()
}
#[test]
fn test_cust_chunk() {
let td = ['A', 'B', 'C', 'D', 'E', 'F'];
// functions as required, but `it` is sampled each time for this to work.
// it would be better to sample the output iterator instead
let mut it = td.iter();
print!("[");
while let Some(val) = cust_chunk(&mut it, 3).next() {
print!("{0}, ", val);
}
println!("]");
// [ABC, DEF, ]
// only samples the output iterator once, then stops. Can you return an iterator
// that would sample the input `it` again on .next()?
// I just wanted to see if I could do this without introducing new types
let mut it = td.iter();
print!("[");
for val in cust_chunk(&mut it, 3) {
print!("{0}, ", val);
}
println!("]");
// [ABC, ]
println!("\n---- end stdout ----");
panic!("stdout")
}
I think what the OP may be wanting are generators, which Rust does have; but for now that feature is not in the stable releases. The code in a generator would look somewhat similar to what's below.
This is one way to implement something that behaves sort of like a generator. It can be invoked as part of a for
loop, and the object it returns (an iterator) will hold its state and return the next chunk of items on each successive call to its .next()
method.
The iterator is automatically generated by the call to std::iter::from_fn()
. The iterator's .next()
method invokes the callback.
fn chunker<I>(chunk_size: usize, data: I)
-> impl Iterator<Item = impl Iterator<Item = I::Item>>
where I: Iterator + Clone,
I::Item: Clone,
{
let mut p = data.peekable();
std::iter::from_fn(
move || {
if p.peek().is_some() {
let c = p.clone();
for _ in 0..chunk_size { if p.next().is_none() { break; } }
Some(c.take(chunk_size))
} else {
None
}
})
}
Usage:
fn main()
{
for chunk in chunker(2, [1, 2, 3, 4, 5, 6].iter()) {
for n in chunk {
print!("{}, ", n);
}
print!("\n");
}
}
Output:
1, 2,
3, 4,
5, 6,
There's some inefficiency in the algorithm above where the iterator is being cloned to create the Take
iterators, then the original is being advanced independently.