Proposal: Option::cow_or_else


#1

I’ve found myself recently hitting places where I had an Option<T> and wanted to pass some other component either a reference to the value in the Some case, or a newly-made default in the other case. This is a great time to use Cow, but the resulting code seems more verbose than the use-case demands:

let foo: Option<String> = Some("hello".into());

foo
    .as_ref()
    .map(Cow::Borrowed)
    .unwrap_or_else(|| Cow::Owned("hi".into()));

// or
foo.as_ref().map_or_else(|| Cow::Owned("hi".into()), Cow::Borrowed);

Proposed New API

Add borrow_or_else<'a, F>(&'a self, f: F) -> Cow<'a, T>, which returns Cow::Borrowed if self was Some, and Cow::Owned if it was None. Similarly, add borrow_or<'a>(&self, fallback: T) -> Cow<'a, T> and borrow_or_default<'a>(&'a self) -> Cow<'a, T> where T : Default.


#2

Since Option has approximately infinity methods and Cow has only two methods, maybe this would be better as a Cow method?

Cow::borrowed_or_else(optionalString, "fallback_value")


#3

Frankly, when using Cow<str> in this way, I tend to write unwrap_or(Cow::Borrowed("..."); in most cases I want to shove a string constant and defer allocation until I actually start writing.


#4

I thought about that, but I previously struggled with using Cow, and I don’t think I would’ve found this had it been a free function on Cow. Also, I think semantically it’s clearer to start from an Option and either borrow it or produce an Owned fallback value.

In my case, there was parsing involved in the fallback branch so the or_else semantic was pretty important, but I think also adding a borrow_or makes sense.


#5

Since this is an or situation, I think Something for coalescing; aka generalized/improved `or_else` would work:

    let foo: Option<String> = Some("hello".into());
    let bar = coalesce!(
        foo.as_ref().map(Into::into),
        Cow::Owned("hi".into()),
    );
    dbg!(bar);

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=d253ee1d1945bc0ad25cdea574ed1ea3

I like that as it’s a more general thing, not a cow-specific additional method.

(Note that the type inference is flowing nicely through the macro – the first argument gets its Cow-ness from the second.)