Skip to content

Commit 67bc866

Browse files
authored
Rollup merge of #82121 - lopopolo:pathbuf-osstring-extend, r=joshtriplett
Implement Extend and FromIterator for OsString Add the following trait impls: - `impl Extend<OsString> for OsString` - `impl<'a> Extend<&'a OsStr> for OsString` - `impl FromIterator<OsString> for OsString` - `impl<'a> FromIterator<&'a OsStr> for OsString` Because `OsString` is a platform string with no particular semantics, concatenating them together seems acceptable. I came across a use case for these trait impls in artichoke/artichoke#1089: Artichoke is a Ruby interpreter. Its CLI accepts multiple `-e` switches for executing inline Ruby code, like: ```console $ cargo -q run --bin artichoke -- -e '2.times {' -e 'puts "foo: #{__LINE__}"' -e '}' foo: 2 foo: 2 ``` I use `clap` for command line argument parsing, which collects these `-e` commands into a `Vec<OsString>`. To pass these commands to the interpreter for `Eval`, I need to join them together. Combining these impls with `Iterator::intersperse` #79524 would enable me to build a single bit of Ruby code. Currently, I'm doing something like: ```rust let mut commands = commands.into_iter(); let mut buf = if let Some(command) = commands.next() { command } else { return Ok(Ok(())); }; for command in commands { buf.push("\n"); buf.push(command); } ``` If there's interest, I'd also like to add impls for `Cow<'a, OsStr>`, which would avoid allocating the `"\n"` `OsString` in the concatenate + intersperse use case.
2 parents 6caa350 + 05ea200 commit 67bc866

File tree

1 file changed

+86
-0
lines changed

1 file changed

+86
-0
lines changed

library/std/src/ffi/os_str.rs

+86
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::borrow::{Borrow, Cow};
55
use crate::cmp;
66
use crate::fmt;
77
use crate::hash::{Hash, Hasher};
8+
use crate::iter::{Extend, FromIterator};
89
use crate::ops;
910
use crate::rc::Rc;
1011
use crate::str::FromStr;
@@ -1192,3 +1193,88 @@ impl FromStr for OsString {
11921193
Ok(OsString::from(s))
11931194
}
11941195
}
1196+
1197+
#[stable(feature = "osstring_extend", since = "1.52.0")]
1198+
impl Extend<OsString> for OsString {
1199+
#[inline]
1200+
fn extend<T: IntoIterator<Item = OsString>>(&mut self, iter: T) {
1201+
for s in iter {
1202+
self.push(&s);
1203+
}
1204+
}
1205+
}
1206+
1207+
#[stable(feature = "osstring_extend", since = "1.52.0")]
1208+
impl<'a> Extend<&'a OsStr> for OsString {
1209+
#[inline]
1210+
fn extend<T: IntoIterator<Item = &'a OsStr>>(&mut self, iter: T) {
1211+
for s in iter {
1212+
self.push(s);
1213+
}
1214+
}
1215+
}
1216+
1217+
#[stable(feature = "osstring_extend", since = "1.52.0")]
1218+
impl<'a> Extend<Cow<'a, OsStr>> for OsString {
1219+
#[inline]
1220+
fn extend<T: IntoIterator<Item = Cow<'a, OsStr>>>(&mut self, iter: T) {
1221+
for s in iter {
1222+
self.push(&s);
1223+
}
1224+
}
1225+
}
1226+
1227+
#[stable(feature = "osstring_extend", since = "1.52.0")]
1228+
impl FromIterator<OsString> for OsString {
1229+
#[inline]
1230+
fn from_iter<I: IntoIterator<Item = OsString>>(iter: I) -> Self {
1231+
let mut iterator = iter.into_iter();
1232+
1233+
// Because we're iterating over `OsString`s, we can avoid at least
1234+
// one allocation by getting the first string from the iterator
1235+
// and appending to it all the subsequent strings.
1236+
match iterator.next() {
1237+
None => OsString::new(),
1238+
Some(mut buf) => {
1239+
buf.extend(iterator);
1240+
buf
1241+
}
1242+
}
1243+
}
1244+
}
1245+
1246+
#[stable(feature = "osstring_extend", since = "1.52.0")]
1247+
impl<'a> FromIterator<&'a OsStr> for OsString {
1248+
#[inline]
1249+
fn from_iter<I: IntoIterator<Item = &'a OsStr>>(iter: I) -> Self {
1250+
let mut buf = Self::new();
1251+
for s in iter {
1252+
buf.push(s);
1253+
}
1254+
buf
1255+
}
1256+
}
1257+
1258+
#[stable(feature = "osstring_extend", since = "1.52.0")]
1259+
impl<'a> FromIterator<Cow<'a, OsStr>> for OsString {
1260+
#[inline]
1261+
fn from_iter<I: IntoIterator<Item = Cow<'a, OsStr>>>(iter: I) -> Self {
1262+
let mut iterator = iter.into_iter();
1263+
1264+
// Because we're iterating over `OsString`s, we can avoid at least
1265+
// one allocation by getting the first owned string from the iterator
1266+
// and appending to it all the subsequent strings.
1267+
match iterator.next() {
1268+
None => OsString::new(),
1269+
Some(Cow::Owned(mut buf)) => {
1270+
buf.extend(iterator);
1271+
buf
1272+
}
1273+
Some(Cow::Borrowed(buf)) => {
1274+
let mut buf = OsString::from(buf);
1275+
buf.extend(iterator);
1276+
buf
1277+
}
1278+
}
1279+
}
1280+
}

0 commit comments

Comments
 (0)