OsString + OsStr = Error

OsSting and OsStr are parallels to String and &str but you can't at the moment seem to add an OsStr to an OsString with the '+' operator.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9a1e2c4e210355736b79228cce2207dc

For consistency this should work shouldn't it? If we agree this should work I'll raise an issue and try and get it fixed. Or is there some technical reason it hasn't or couldn't be done?

Or is it that the compiler should have a very customised error here?

It looks like you're trying to concat OsStrings here. Maybe you should be using path.join()?

Having String : Add<...> (in this case, String : Add<&'_ str>) is already something very controversial:

  • not only does it hide important implementation details such as how the memory is (re)allocated (should it .to_owned() or mutate the given strings?), which for a systems programming language is surprising,

  • it also relies on an opinionated choice of semantics of + for two sequences of elements: should it

    • concatenate the two sequences, or should it

    • add each pair of elements?

    Regarding String it may be indeed more plausible to choose the former (given that there is no char : Add<char>), but with things like a Vec<u8> the line gets more blurry.

So, all in all, String : Add<&'_ str> was added to make the language more friendly to newcomers, but at the cost of such API being inconsistent with most of the design of the language. Hence its not existing for other similar types such as OsStr{,ing}, since these are not as pervasive as Str{,ing} are.


That being said, I think we all agree that a (Self, &Self) -> Self can be a more ergonomic API than (&mut Self, &Self) -> (), since the former lets you chain mutations (although such thing may be less efficient than a bulk concatenation, such as with .join()), and this can be achieved very easily by adding your own method on the desired type:

use ::std::{
    borrow::Borrow,
    ffi::{OsStr, OsString},
};

trait Append<Segment : ?Sized> : Sized
where
    Segment : ToOwned<Owned = Self>,
    Self : Borrow<Segment>,
{
    fn append (self: Self, s: impl AsRef<Segment>)
      -> Self
    ;
}

impl Append<OsStr> for OsString {
    fn append (mut self: OsString, s: impl AsRef<OsStr>)
      -> Self
    {
        self.push(s);
        self
    }
}

fn main ()
{
    let joined =
        OsString::new()
            .append(OsString::new().as_os_str())
            .append(OsString::new().as_os_str())
            .append(OsString::new().as_os_str())
    ;
}
1 Like