AFAIK scan
iterator method is the only iterator method that provides a mechanism to share some mutable state across iterations. But I think this method is too complicated and wide, therefore it should be replaced by more convenient and simple methods.
Let's look at scan
usage example:
let a = [1, 2, 3];
let mut iter = a.iter().scan(1, |state, &x| {
// each iteration, we'll multiply the state by the element
*state = *state * x;
// then, we'll yield the negation of the state
Some(-*state)
});
assert_eq!(iter.next(), Some(-1));
assert_eq!(iter.next(), Some(-2));
assert_eq!(iter.next(), Some(-6));
assert_eq!(iter.next(), None);
Observations:
- We must provide an initial value of the
state
, we can't get it from the first iteration. -
state
is provided by&mut
reference, so we must change it by using*state = ...
syntax, not by returning a value. So, we always need curly brackets. But it's a powerful tool: we can compute a return value before changing state, or after. - Then we return
Some()
of next value. And by this we do as same asfilter_map
. Because of thisScan
doesn't implementExactSizeIterator
, so we can't use it to allocate exact space when using thecollect
method.
I think:
- We should able to provide initial value either by telling an exact value or from first iteration.
-
state
should be changed not by mutable reference, but by functionFn(State, &Item) -> State
, because it is more simple and don't require{}
in simple cases. - There shouldn't be
filter_map
ormap
orfilter
behavior inside this iterator, it should be done by the next iterator adaptors.
So, I suggest these new methods to replace scan
: state_before
, state_after
with equal signatures.
Example:
let a = [1, 2, 3];
assert_eq!(a.iter().state_before(None, |state, &item| state * item).eq(&[(1, 1), (2, 2), (6, 3)]));
assert_eq!(a.iter().state_before(Some(4), |state, &item| state * item).eq(&[(4, 1), (8, 2), (24, 3)]));
assert_eq!(a.iter().state_after(None, |state, &item| state * item).eq(&[(1, 1), (1, 2), (2, 3)]));
assert_eq!(a.iter().state_after(Some(4), |state, &item| state * item).eq(&[(4, 1), (4, 2), (8, 3)]));
- First argument of these methods - is an initial value. If it is
None
, then the initial value is yielded from the first iteration. If it isSome(...)
, then it initialized by the inner value. - Second arguments of this methods - is function
Fn(State, &Item) -> State
. That function receives current state and current item by reference and it must return next state value. - The main name is
state
because these methods provide state across iterations. - Suffix
before
means that the state will be modified before returning the element. Suffixafter
means that the state will be modified after returning the element. - These methods return an iterator with
Item = (State, OldItem)
by analogy asenumerate
. - These methods return an iterator that implements
ExactSizeIterator
. - Then, we can apply
filter_map
next and get the same behavior as ascan
.
What do you think?