Skip to content

Commit

Permalink
Rollup merge of rust-lang#56548 - Lucretiel:string-extend-optimize, r…
Browse files Browse the repository at this point in the history
…=sfackler

Optimized string FromIterator + Extend impls

I noticed that there was a lost opportunity to reuse string buffers in `FromIterator<String>` and `FromIterator<Cow<str>>`; updated the implementations to use these. In practice this translates to at least one fewer allocation when using these APIs.

Additionally, rewrote `Extend` implementations to use `iter.for_each`, which (supposedly) helps the compiler optimize those loops (because iterator adapters are encouraged to provide optimized implementations of `fold` and `try_fold`.
  • Loading branch information
pietroalbini authored Dec 6, 2018
2 parents 9e7ff56 + 811a2bf commit e9e92d5
Showing 1 changed file with 29 additions and 18 deletions.
47 changes: 29 additions & 18 deletions src/liballoc/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1732,18 +1732,37 @@ impl<'a> FromIterator<&'a str> for String {
#[stable(feature = "extend_string", since = "1.4.0")]
impl FromIterator<String> for String {
fn from_iter<I: IntoIterator<Item = String>>(iter: I) -> String {
let mut buf = String::new();
buf.extend(iter);
buf
let mut iterator = iter.into_iter();

// Because we're iterating over `String`s, we can avoid at least
// one allocation by getting the first string from the iterator
// and appending to it all the subsequent strings.
match iterator.next() {
None => String::new(),
Some(mut buf) => {
buf.extend(iterator);
buf
}
}
}
}

#[stable(feature = "herd_cows", since = "1.19.0")]
impl<'a> FromIterator<Cow<'a, str>> for String {
fn from_iter<I: IntoIterator<Item = Cow<'a, str>>>(iter: I) -> String {
let mut buf = String::new();
buf.extend(iter);
buf
let mut iterator = iter.into_iter();

// Because we're iterating over CoWs, we can (potentially) avoid at least
// one allocation by getting the first item and appending to it all the
// subsequent items.
match iterator.next() {
None => String::new(),
Some(cow) => {
let mut buf = cow.into_owned();
buf.extend(iterator);
buf
}
}
}
}

Expand All @@ -1753,9 +1772,7 @@ impl Extend<char> for String {
let iterator = iter.into_iter();
let (lower_bound, _) = iterator.size_hint();
self.reserve(lower_bound);
for ch in iterator {
self.push(ch)
}
iterator.for_each(move |c| self.push(c));
}
}

Expand All @@ -1769,27 +1786,21 @@ impl<'a> Extend<&'a char> for String {
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a> Extend<&'a str> for String {
fn extend<I: IntoIterator<Item = &'a str>>(&mut self, iter: I) {
for s in iter {
self.push_str(s)
}
iter.into_iter().for_each(move |s| self.push_str(s));
}
}

#[stable(feature = "extend_string", since = "1.4.0")]
impl Extend<String> for String {
fn extend<I: IntoIterator<Item = String>>(&mut self, iter: I) {
for s in iter {
self.push_str(&s)
}
iter.into_iter().for_each(move |s| self.push_str(&s));
}
}

#[stable(feature = "herd_cows", since = "1.19.0")]
impl<'a> Extend<Cow<'a, str>> for String {
fn extend<I: IntoIterator<Item = Cow<'a, str>>>(&mut self, iter: I) {
for s in iter {
self.push_str(&s)
}
iter.into_iter().for_each(move |s| self.push_str(&s));
}
}

Expand Down

0 comments on commit e9e92d5

Please sign in to comment.