Skip to content

Commit ab1bd3a

Browse files
committed
core: optimize {option,result}::collect
The bug #11084 causes these collect functions to run about twice as slow as they should because llvm is having trouble optimizing away the closure for some reason. This patch works around that performance bug by using a simple adapter iterator explicitly for capturing if the outer iterator returns an error.
1 parent 1ea9991 commit ab1bd3a

File tree

2 files changed

+44
-20
lines changed

2 files changed

+44
-20
lines changed

src/libcore/option.rs

+22-10
Original file line numberDiff line numberDiff line change
@@ -587,20 +587,32 @@ impl<A> ExactSize<A> for Item<A> {}
587587
/// ```
588588
#[inline]
589589
pub fn collect<T, Iter: Iterator<Option<T>>, V: FromIterator<T>>(iter: Iter) -> Option<V> {
590-
// FIXME(#11084): This should be twice as fast once this bug is closed.
591-
let mut iter = iter.scan(false, |state, x| {
592-
match x {
593-
Some(x) => Some(x),
594-
None => {
595-
*state = true;
596-
None
590+
// FIXME(#11084): This could be replaced with Iterator::scan when this
591+
// performance bug is closed.
592+
593+
struct Adapter<Iter> {
594+
iter: Iter,
595+
found_none: bool,
596+
}
597+
598+
impl<T, Iter: Iterator<Option<T>>> Iterator<T> for Adapter<Iter> {
599+
#[inline]
600+
fn next(&mut self) -> Option<T> {
601+
match self.iter.next() {
602+
Some(Some(value)) => Some(value),
603+
Some(None) => {
604+
self.found_none = true;
605+
None
606+
}
607+
None => None,
597608
}
598609
}
599-
});
610+
}
600611

601-
let v: V = FromIterator::from_iter(iter.by_ref());
612+
let mut adapter = Adapter { iter: iter, found_none: false };
613+
let v: V = FromIterator::from_iter(adapter.by_ref());
602614

603-
if iter.state {
615+
if adapter.found_none {
604616
None
605617
} else {
606618
Some(v)

src/libcore/result.rs

+22-10
Original file line numberDiff line numberDiff line change
@@ -585,20 +585,32 @@ impl<T: Show, E> Result<T, E> {
585585
/// ```
586586
#[inline]
587587
pub fn collect<T, E, Iter: Iterator<Result<T, E>>, V: FromIterator<T>>(iter: Iter) -> Result<V, E> {
588-
// FIXME(#11084): This should be twice as fast once this bug is closed.
589-
let mut iter = iter.scan(None, |state, x| {
590-
match x {
591-
Ok(x) => Some(x),
592-
Err(err) => {
593-
*state = Some(err);
594-
None
588+
// FIXME(#11084): This could be replaced with Iterator::scan when this
589+
// performance bug is closed.
590+
591+
struct Adapter<Iter, E> {
592+
iter: Iter,
593+
err: Option<E>,
594+
}
595+
596+
impl<T, E, Iter: Iterator<Result<T, E>>> Iterator<T> for Adapter<Iter, E> {
597+
#[inline]
598+
fn next(&mut self) -> Option<T> {
599+
match self.iter.next() {
600+
Some(Ok(value)) => Some(value),
601+
Some(Err(err)) => {
602+
self.err = Some(err);
603+
None
604+
}
605+
None => None,
595606
}
596607
}
597-
});
608+
}
598609

599-
let v: V = FromIterator::from_iter(iter.by_ref());
610+
let mut adapter = Adapter { iter: iter, err: None };
611+
let v: V = FromIterator::from_iter(adapter.by_ref());
600612

601-
match iter.state {
613+
match adapter.err {
602614
Some(err) => Err(err),
603615
None => Ok(v),
604616
}

0 commit comments

Comments
 (0)