Skip to content

Add try_all, try_any, try_position and try_rposition methods to Iterator trait #361

Closed
@rodrigocfd

Description

@rodrigocfd

Proposal

Problem statement

Currently there is no simple way to use all(), any(), position() and rposition() if the predicate can fail, returning an Err.

Motivating examples or use cases

This proposal came from an StackOverflow question, which asks for a fallible method for position(). The answer is rather convoluted when compared to the simplicity of try_for_each() versus for_each().

The original question is:

let items: &[Result<&str, u32>] = &[Ok("foo"), Err(444), Ok("bar")];

let bar_idx = items.iter()
    .position(|item| item? == "bar")?; // what to do here?

Solution sketch

I sketched a FooIterator with the aforementioned methods so I could use them right away, but I suppose they should be members of Iterator. Also, I'm aware the implementation below is far from being standardized:

pub trait FooIterator: Iterator {
	fn try_all<E, F>(&mut self, mut predicate: F) -> Result<bool, E>
		where Self: Sized,
			F: FnMut(Self::Item) -> Result<bool, E>,
	{
		for item in self {
			if !predicate(item)? {
				return Ok(false)
			}
		}
		Ok(true)
	}

	fn try_any<E, F>(&mut self, mut predicate: F) -> Result<bool, E>
		where Self: Sized,
			F: FnMut(Self::Item) -> Result<bool, E>,
	{
		for item in self {
			if predicate(item)? {
				return Ok(true)
			}
		}
		Ok(false)
	}

	fn try_position<E, F>(&mut self, mut predicate: F) -> Result<Option<usize>, E>
		where Self: Sized,
			F: FnMut(Self::Item) -> Result<bool, E>,
	{
		for (idx, item) in self.enumerate() {
			if predicate(item)? {
				return Ok(Some(idx));
			}
		}
		Ok(None)
	}

	fn try_rposition<E, F>(&mut self, mut predicate: F) -> Result<Option<usize>, E>
		where Self: Sized + DoubleEndedIterator,
			F: FnMut(Self::Item) -> Result<bool, E>,
	{
		for (idx, item) in self.rev().enumerate() {
			if predicate(item)? {
				return Ok(Some(idx));
			}
		}
		Ok(None)
	}
}

impl<'a, T> TryIterator for core::slice::Iter<'a, T> {}
impl<I> TryIterator for std::iter::Enumerate<I> where I: Iterator {}
impl<I> TryIterator for std::iter::Skip<I> where I: Iterator {}
impl<I> TryIterator for std::iter::StepBy<I> where I: Iterator {}
impl<I> TryIterator for std::iter::Take<I> where I: Iterator {}

Alternatives

I implemented and published the TryIterator crate, so I could use these methods immediately. But I believe these methods have their place in the standard library.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ACP-acceptedAPI Change Proposal is accepted (seconded with no objections)T-libs-apiapi-change-proposalA proposal to add or alter unstable APIs in the standard libraries

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions