Add some methods for std::borrow::Cow

Add some methods for std::borrow::Cow

The cause of the problem:

  • I was writing a program to launch an external program. When adding a large number of parameters, I wanted to convert Iterator<Item=Cow<'_, ...>> to Iterator<Item=Cow<'_, OsStr>>, but I had to write the match expression every time. It was too cumbersome.
  • format!() returns a String, and when using a path, there is a PathBuf, but Command::args requires an IntoIterator<Item=impl AsRef<OsStr>>.

Here are some problems:

  • Is it possible to remove the lifetime 'b?
  • For consistency, should we add the same functionality to Result?
  • Since the transformations of Cow must be performed concurrently, it is not possible to have map and map_err like Result.
  • If you want to rename it, what should it be called?
  • Why not add From<&str> for &OsStr?
    fn inner_into_os_str<'a, 'b, T: ToOwned>(source: Cow<'a, T>) -> Cow<'b, OsStr>
    where
      T::Owned: Into<OsString>,
      &'a T: Into<&'b OsStr>,
    {
      // But the trait `From<&str>` is not implemented for `&OsStr` !!!
      source.map(copy_fn!(Into::into)) // source.map(Into::<&'b OsStr>::into, Into::<OsString>::into)
    }
    

Example

use std::borrow::Cow;
use std::ffi::OsStr;

pub trait CowExt<'a, T: ?Sized + ToOwned + 'a>
{
  fn map_ref_or_owned<'b, U: ?Sized + ToOwned, B, O>(self, b: B, o: O) -> Cow<'b, U>
  where
    B: FnOnce(&'a T) -> &'b U,
    O: FnOnce(T::Owned) -> U::Owned;
  fn map_to_cow<'b, U: ?Sized + ToOwned, B, O>(self, b: B, o: O) -> Cow<'b, U>
  where
    B: FnOnce(&'a T) -> Cow<'b, U>,
    O: FnOnce(T::Owned) -> Cow<'b, U>;
}
impl<'a, T: ?Sized + ToOwned> CowExt<'a, T> for Cow<'a, T>
{
  fn map_ref_or_owned<'b, U: ?Sized + ToOwned, B, O>(self, b: B, o: O) -> Cow<'b, U>
  where
    B: FnOnce(&'a T) -> &'b U,
    O: FnOnce(T::Owned) -> U::Owned,
  {
    match self
    {
      Cow::Borrowed(borrow) => Cow::Borrowed(b(borrow)),
      Cow::Owned(owned) => Cow::Owned(o(owned)),
    }
  }

  fn map_to_cow<'b, U: ?Sized + ToOwned, B, O>(self, b: B, o: O) -> Cow<'b, U>
  where
    B: FnOnce(&'a T) -> Cow<'b, U>,
    O: FnOnce(T::Owned) -> Cow<'b, U>,
  {
    match self
    {
      Cow::Borrowed(borrow) => b(borrow),
      Cow::Owned(owned) => o(owned),
    }
  }
}
fn main(){
  let cow = Cow::Borrow("test");
  let cow : Cow<'_, OsStr> = cow.map_ref_or_owned(AsRef::as_ref, Into::into);
  let cow = Cow::Borrow(std::path::Path::new("test/"));
  let cow : Cow<'_, OsStr> = cow.map_ref_or_owned(AsRef::as_ref, Into::into);
}

This one isn’t really about Cow, but it’s also the easiest to answer: not every OsStr is valid UTF-8, meaning not every OsStr is a valid str. You have to choose whether you want to_str/into_string behavior or to_string_lossy behavior. (OsString has most of the docs about this, rather than OsStr.)

CodesInChaos is right, I read this backwards ><

You're talking about the &OsStr -> &str direction. Every &str is a valid &OsStr, so From<&str> for &OsStr should be fine. Perhaps it wasn't added because it's redundant with the existing AsRef<OsStr> for str?

1 Like

You may be right, but we have str::to_string & <str as ToOwned>::to_owned & <&str as Into::<String>>::into & <String as From::<&str>>::from and so on... They all have exactly the same functionality. Has this been forgotten?