Is there a way to use iterator do the same with the behavior in expected().
I know intersperse((0..2).map(|_| 'a'), 'b') can work, but I'm asking for both push operations in Self and Separatior being behind a callback.
#![feature(iter_intersperse)]
use itertools::intersperse_with as inter;
use std::cell::RefCell;
use std::iter::from_fn;
fn itertools_() {
let buf = RefCell::new(String::new());
let a = || Some(buf.borrow_mut().push('a'));
let b = || buf.borrow_mut().push('b');
inter(from_fn(a).take(2), b).last();
dbg!(&buf);
}
fn std_() {
let buf = RefCell::new(String::new());
let a = || Some(buf.borrow_mut().push('a'));
let b = || buf.borrow_mut().push('b');
from_fn(a).take(2).intersperse_with(b).last();
dbg!(&buf);
}
fn main() {
itertools_(); // unexpected: aab
std_(); // unexpected: aab
expected(|buf| buf.push('a'), |buf| buf.push('b')); // expected: aba
}
fn expected(a: impl Fn(&mut String), b: impl Fn(&mut String)) {
let mut buf = String::new();
let write_a_times = 2usize;
let write_b_times = write_a_times.saturating_sub(1);
let mut b_cnt = 0;
for _ in 0..write_a_times {
a(&mut buf);
if b_cnt < write_b_times {
b(&mut buf);
}
b_cnt += 1;
}
dbg!(&buf);
}
When I modify the number to 5, the result becomes abaaaa
But after tweaking your code, I got almost desired result ababababab (unfortunately with an extra trailling b). Rust Playground
let flag = Cell::new(true);
let a = || {
flag.set(true);
Some(buf.borrow_mut().push('a'))
};
let b = || Some(
if flag.replace(false) {
buf.borrow_mut().push('b')
}
);
intersperse_with does not fit well for iterators that iterate over side-effect closures I guess...
I'll insist on for-loop + condition check without RefCell.
let iters = 5;
let buf = RefCell::new(String::new());
let a = || buf.borrow_mut().push('a');
let b = || buf.borrow_mut().push('b');
a();
for _ in 1..iters {
b();
a();
}
If you need to have only one called per iteration, you can do this:
let iters = 5;
let buf = &RefCell::new(String::new());
let closure_maker = |c| move || buf.borrow_mut().push(c);
let a = closure_maker('a');
let b = closure_maker('b');
std::iter::repeat([&b, &a])
.take(iters)
.flatten()
.skip(1)
.for_each(|f| f());
If the closures aren't the same closure type, you'll need to turn them into trait objects, at which point it would probably be better to have
std::iter::repeat([false, true])
.take(iters)
.flatten()
.skip(1)
.for_each(|t| if t { a() } else { b() });
P.S. If you have an infallible from_fn, you can use repeat_with instead.