Skip to content

Commit 47d5196

Browse files
committed
Add a try_collect() helper method to Iterator
Tweaked `try_collect()` to accept more `Try` types Updated feature attribute for tracking issue
1 parent f90b06d commit 47d5196

File tree

3 files changed

+129
-0
lines changed

3 files changed

+129
-0
lines changed

Diff for: library/core/src/iter/traits/iterator.rs

+82
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::cmp::{self, Ordering};
22
use crate::ops::{ChangeOutputType, ControlFlow, FromResidual, Residual, Try};
33

4+
use super::super::try_process;
45
use super::super::TrustedRandomAccessNoCoerce;
56
use super::super::{Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse};
67
use super::super::{FlatMap, Flatten};
@@ -1777,6 +1778,87 @@ pub trait Iterator {
17771778
FromIterator::from_iter(self)
17781779
}
17791780

1781+
/// Fallibly transforms an iterator into a collection, short circuiting if
1782+
/// a failure is encountered.
1783+
///
1784+
/// `try_collect()` is a variation of [`collect()`][`collect`] that allows fallible
1785+
/// conversions during collection. Its main use case is simplifying conversions from
1786+
/// iterators yielding [`Option<T>`][`Option`] into `Option<Collection<T>>`, or similarly for other [`Try`]
1787+
/// types (e.g. [`Result`]).
1788+
///
1789+
/// Importantly, `try_collect()` doesn't require that the outer [`Try`] type also implements [`FromIterator`];
1790+
/// only the inner type produced on `Try::Output` must implement it. Concretely,
1791+
/// this means that collecting into `ControlFlow<_, Vec<i32>>` is valid because `Vec<i32>` implements
1792+
/// [`FromIterator`], even though [`ControlFlow`] doesn't.
1793+
///
1794+
/// Also, if a failure is encountered during `try_collect()`, the iterator is still valid and
1795+
/// may continue to be used, in which case it will continue iterating starting after the element that
1796+
/// triggered the failure. See the last example below for an example of how this works.
1797+
///
1798+
/// # Examples
1799+
/// Successfully collecting an iterator of `Option<i32>` into `Option<Vec<i32>>`:
1800+
/// ```
1801+
/// #![feature(iterator_try_collect)]
1802+
///
1803+
/// let u = vec![Some(1), Some(2), Some(3)];
1804+
/// let v = u.into_iter().try_collect::<Vec<i32>>();
1805+
/// assert_eq!(v, Some(vec![1, 2, 3]));
1806+
/// ```
1807+
///
1808+
/// Failing to collect in the same way:
1809+
/// ```
1810+
/// #![feature(iterator_try_collect)]
1811+
///
1812+
/// let u = vec![Some(1), Some(2), None, Some(3)];
1813+
/// let v = u.into_iter().try_collect::<Vec<i32>>();
1814+
/// assert_eq!(v, None);
1815+
/// ```
1816+
///
1817+
/// A similar example, but with `Result`:
1818+
/// ```
1819+
/// #![feature(iterator_try_collect)]
1820+
///
1821+
/// let u: Vec<Result<i32, ()>> = vec![Ok(1), Ok(2), Ok(3)];
1822+
/// let v = u.into_iter().try_collect::<Vec<i32>>();
1823+
/// assert_eq!(v, Ok(vec![1, 2, 3]));
1824+
///
1825+
/// let u = vec![Ok(1), Ok(2), Err(()), Ok(3)];
1826+
/// let v = u.into_iter().try_collect::<Vec<i32>>();
1827+
/// assert_eq!(v, Err(()));
1828+
/// ```
1829+
///
1830+
/// Finally, even [`ControlFlow`] works, despite the fact that it
1831+
/// doesn't implement [`FromIterator`]. Note also that the iterator can
1832+
/// continue to be used, even if a failure is encountered:
1833+
///
1834+
/// ```
1835+
/// #![feature(iterator_try_collect)]
1836+
///
1837+
/// use core::ops::ControlFlow::{Break, Continue};
1838+
///
1839+
/// let u = [Continue(1), Continue(2), Break(3), Continue(4), Continue(5)];
1840+
/// let mut it = u.into_iter();
1841+
///
1842+
/// let v = it.try_collect::<Vec<_>>();
1843+
/// assert_eq!(v, Break(3));
1844+
///
1845+
/// let v = it.try_collect::<Vec<_>>();
1846+
/// assert_eq!(v, Continue(vec![4, 5]));
1847+
/// ```
1848+
///
1849+
/// [`collect`]: Iterator::collect
1850+
#[inline]
1851+
#[unstable(feature = "iterator_try_collect", issue = "94047")]
1852+
fn try_collect<B>(&mut self) -> ChangeOutputType<Self::Item, B>
1853+
where
1854+
Self: Sized,
1855+
<Self as Iterator>::Item: Try,
1856+
<<Self as Iterator>::Item as Try>::Residual: Residual<B>,
1857+
B: FromIterator<<Self::Item as Try>::Output>,
1858+
{
1859+
try_process(self, |i| i.collect())
1860+
}
1861+
17801862
/// Consumes an iterator, creating two collections from it.
17811863
///
17821864
/// The predicate passed to `partition()` can return `true`, or `false`.

Diff for: library/core/tests/iter/traits/iterator.rs

+46
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,52 @@ fn test_collect() {
497497
assert!(a == b);
498498
}
499499

500+
#[test]
501+
fn test_try_collect() {
502+
use core::ops::ControlFlow::{Break, Continue};
503+
504+
let u = vec![Some(1), Some(2), Some(3)];
505+
let v = u.into_iter().try_collect::<Vec<i32>>();
506+
assert_eq!(v, Some(vec![1, 2, 3]));
507+
508+
let u = vec![Some(1), Some(2), None, Some(3)];
509+
let mut it = u.into_iter();
510+
let v = it.try_collect::<Vec<i32>>();
511+
assert_eq!(v, None);
512+
let v = it.try_collect::<Vec<i32>>();
513+
assert_eq!(v, Some(vec![3]));
514+
515+
let u: Vec<Result<i32, ()>> = vec![Ok(1), Ok(2), Ok(3)];
516+
let v = u.into_iter().try_collect::<Vec<i32>>();
517+
assert_eq!(v, Ok(vec![1, 2, 3]));
518+
519+
let u = vec![Ok(1), Ok(2), Err(()), Ok(3)];
520+
let v = u.into_iter().try_collect::<Vec<i32>>();
521+
assert_eq!(v, Err(()));
522+
523+
let numbers = vec![1, 2, 3, 4, 5];
524+
let all_positive = numbers
525+
.iter()
526+
.cloned()
527+
.map(|n| if n > 0 { Some(n) } else { None })
528+
.try_collect::<Vec<i32>>();
529+
assert_eq!(all_positive, Some(numbers));
530+
531+
let numbers = vec![-2, -1, 0, 1, 2];
532+
let all_positive =
533+
numbers.into_iter().map(|n| if n > 0 { Some(n) } else { None }).try_collect::<Vec<i32>>();
534+
assert_eq!(all_positive, None);
535+
536+
let u = [Continue(1), Continue(2), Break(3), Continue(4), Continue(5)];
537+
let mut it = u.into_iter();
538+
539+
let v = it.try_collect::<Vec<_>>();
540+
assert_eq!(v, Break(3));
541+
542+
let v = it.try_collect::<Vec<_>>();
543+
assert_eq!(v, Continue(vec![4, 5]));
544+
}
545+
500546
// just tests by whether or not this compiles
501547
fn _empty_impl_all_auto_traits<T>() {
502548
use std::panic::{RefUnwindSafe, UnwindSafe};

Diff for: library/core/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
#![feature(iter_intersperse)]
6868
#![feature(iter_is_partitioned)]
6969
#![feature(iter_order_by)]
70+
#![feature(iterator_try_collect)]
7071
#![feature(iterator_try_reduce)]
7172
#![feature(const_mut_refs)]
7273
#![feature(const_pin)]

0 commit comments

Comments
 (0)