Skip to content

Commit e9e92d5

Browse files
authored
Rollup merge of #56548 - Lucretiel:string-extend-optimize, r=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`.
2 parents 9e7ff56 + 811a2bf commit e9e92d5

File tree

1 file changed

+29
-18
lines changed

1 file changed

+29
-18
lines changed

src/liballoc/string.rs

+29-18
Original file line numberDiff line numberDiff line change
@@ -1732,18 +1732,37 @@ impl<'a> FromIterator<&'a str> for String {
17321732
#[stable(feature = "extend_string", since = "1.4.0")]
17331733
impl FromIterator<String> for String {
17341734
fn from_iter<I: IntoIterator<Item = String>>(iter: I) -> String {
1735-
let mut buf = String::new();
1736-
buf.extend(iter);
1737-
buf
1735+
let mut iterator = iter.into_iter();
1736+
1737+
// Because we're iterating over `String`s, we can avoid at least
1738+
// one allocation by getting the first string from the iterator
1739+
// and appending to it all the subsequent strings.
1740+
match iterator.next() {
1741+
None => String::new(),
1742+
Some(mut buf) => {
1743+
buf.extend(iterator);
1744+
buf
1745+
}
1746+
}
17381747
}
17391748
}
17401749

17411750
#[stable(feature = "herd_cows", since = "1.19.0")]
17421751
impl<'a> FromIterator<Cow<'a, str>> for String {
17431752
fn from_iter<I: IntoIterator<Item = Cow<'a, str>>>(iter: I) -> String {
1744-
let mut buf = String::new();
1745-
buf.extend(iter);
1746-
buf
1753+
let mut iterator = iter.into_iter();
1754+
1755+
// Because we're iterating over CoWs, we can (potentially) avoid at least
1756+
// one allocation by getting the first item and appending to it all the
1757+
// subsequent items.
1758+
match iterator.next() {
1759+
None => String::new(),
1760+
Some(cow) => {
1761+
let mut buf = cow.into_owned();
1762+
buf.extend(iterator);
1763+
buf
1764+
}
1765+
}
17471766
}
17481767
}
17491768

@@ -1753,9 +1772,7 @@ impl Extend<char> for String {
17531772
let iterator = iter.into_iter();
17541773
let (lower_bound, _) = iterator.size_hint();
17551774
self.reserve(lower_bound);
1756-
for ch in iterator {
1757-
self.push(ch)
1758-
}
1775+
iterator.for_each(move |c| self.push(c));
17591776
}
17601777
}
17611778

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

17781793
#[stable(feature = "extend_string", since = "1.4.0")]
17791794
impl Extend<String> for String {
17801795
fn extend<I: IntoIterator<Item = String>>(&mut self, iter: I) {
1781-
for s in iter {
1782-
self.push_str(&s)
1783-
}
1796+
iter.into_iter().for_each(move |s| self.push_str(&s));
17841797
}
17851798
}
17861799

17871800
#[stable(feature = "herd_cows", since = "1.19.0")]
17881801
impl<'a> Extend<Cow<'a, str>> for String {
17891802
fn extend<I: IntoIterator<Item = Cow<'a, str>>>(&mut self, iter: I) {
1790-
for s in iter {
1791-
self.push_str(&s)
1792-
}
1803+
iter.into_iter().for_each(move |s| self.push_str(&s));
17931804
}
17941805
}
17951806

0 commit comments

Comments
 (0)