-
Notifications
You must be signed in to change notification settings - Fork 334
Description
Follow-up to #1046
I'm pondering whether
fn exactly_one(mut self) -> Result<Self::Item, ExactlyOneError<Self>>
should rather be
fn exactly_one(&mut self) -> Result<Self::Item, ExactlyOneError<Self::Item>>
and ExactlyOneError
should contain an Option<[Self::Item; 2]>
, so that the user can inspect the first two elements, and simply re-use the iterator afterwards, if they want to. (Similar to our all_equal_value
.)
I think this is better, because at the moment we must specialize each and every function/trait (e.g. ExactSizeIterator
, DoubleEndedIterator
) to exploit the underlying iterator's specializations.
With my proposal, the user could still do this:
match iter.exactly_one() {
Ok(value) => do_stuff_with(value),
Err(None) => iterator_was_empty(),
Err(Some(values)) => {
// process *all* elements of the original iterator:
values.chain(iter).for_each(...);
}
}
or - terse -:
match iter.exactly_one() {
Ok(value) => do_stuff_with(value),
Err(optional_2_values) => {
// process *all* elements of the original iterator:
optional_2_values.into_iter().flatten().chain(iter).for_each(...);
}
}
That is, we do not lose functionality, but the callers profit all specializations given by std
.
Note: From my experience, re-visiting all elements of the original iterator is very rare. In most cases, it only matters if exactly_one
worked or not, and if it did not work, the elements themselves are not relevant.
Following similar reasoning, I advocate for fn at_most_one(&mut self) -> Result<Option<Item>, AtMostOneError<Self::Item>>
where AtMostOneError
wraps a [Self::Item; 2]
.
@jswrenn I'd appreciate your opinion on this.