Skip to content

ExactlyOneError should not iterate itself #1047

@phimuemue

Description

@phimuemue

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions