diff options
| author | Yuki Okushi <huyuumi.dev@gmail.com> | 2021-03-14 13:07:28 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-03-14 13:07:28 +0900 |
| commit | 67bc866e5934e19c37fd7a115cdc8142fcb8c51d (patch) | |
| tree | 866b0e116a0ac14b46c453443ed36e28ac65d1c4 | |
| parent | 6caa350503044420d4fd0cc324b3295a0c19cc70 (diff) | |
| parent | 05ea200213fe30967fff38ebac887c52803aabd5 (diff) | |
| download | rust-67bc866e5934e19c37fd7a115cdc8142fcb8c51d.tar.gz rust-67bc866e5934e19c37fd7a115cdc8142fcb8c51d.zip | |
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 https://github.com/artichoke/artichoke/pull/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` https://github.com/rust-lang/rust/issues/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.
| -rw-r--r-- | library/std/src/ffi/os_str.rs | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 272eccda894..ce52ffc0241 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -5,6 +5,7 @@ use crate::borrow::{Borrow, Cow}; use crate::cmp; use crate::fmt; use crate::hash::{Hash, Hasher}; +use crate::iter::{Extend, FromIterator}; use crate::ops; use crate::rc::Rc; use crate::str::FromStr; @@ -1192,3 +1193,88 @@ impl FromStr for OsString { Ok(OsString::from(s)) } } + +#[stable(feature = "osstring_extend", since = "1.52.0")] +impl Extend<OsString> for OsString { + #[inline] + fn extend<T: IntoIterator<Item = OsString>>(&mut self, iter: T) { + for s in iter { + self.push(&s); + } + } +} + +#[stable(feature = "osstring_extend", since = "1.52.0")] +impl<'a> Extend<&'a OsStr> for OsString { + #[inline] + fn extend<T: IntoIterator<Item = &'a OsStr>>(&mut self, iter: T) { + for s in iter { + self.push(s); + } + } +} + +#[stable(feature = "osstring_extend", since = "1.52.0")] +impl<'a> Extend<Cow<'a, OsStr>> for OsString { + #[inline] + fn extend<T: IntoIterator<Item = Cow<'a, OsStr>>>(&mut self, iter: T) { + for s in iter { + self.push(&s); + } + } +} + +#[stable(feature = "osstring_extend", since = "1.52.0")] +impl FromIterator<OsString> for OsString { + #[inline] + fn from_iter<I: IntoIterator<Item = OsString>>(iter: I) -> Self { + let mut iterator = iter.into_iter(); + + // Because we're iterating over `OsString`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 => OsString::new(), + Some(mut buf) => { + buf.extend(iterator); + buf + } + } + } +} + +#[stable(feature = "osstring_extend", since = "1.52.0")] +impl<'a> FromIterator<&'a OsStr> for OsString { + #[inline] + fn from_iter<I: IntoIterator<Item = &'a OsStr>>(iter: I) -> Self { + let mut buf = Self::new(); + for s in iter { + buf.push(s); + } + buf + } +} + +#[stable(feature = "osstring_extend", since = "1.52.0")] +impl<'a> FromIterator<Cow<'a, OsStr>> for OsString { + #[inline] + fn from_iter<I: IntoIterator<Item = Cow<'a, OsStr>>>(iter: I) -> Self { + let mut iterator = iter.into_iter(); + + // Because we're iterating over `OsString`s, we can avoid at least + // one allocation by getting the first owned string from the iterator + // and appending to it all the subsequent strings. + match iterator.next() { + None => OsString::new(), + Some(Cow::Owned(mut buf)) => { + buf.extend(iterator); + buf + } + Some(Cow::Borrowed(buf)) => { + let mut buf = OsString::from(buf); + buf.extend(iterator); + buf + } + } + } +} |
