nnnmmm
nnnmmm

Reputation: 8764

Trait for conversion of references and owned values to Cow

I have a function that can handle both owned and borrowed values of some type Foo by accepting a Cow<'_, Foo>. However, I'd like to make it more convenient by allowing to pass in owned or borrowed Foos directly.

Is it possible to have a conversion trait to Cow that's implemented both on a reference type and its owned version?

This is what I tried:

trait Convert<'a> : Clone {
    fn into_cow(self) -> Cow<'a, Self>;
}

// Works
impl<'a> Convert<'a> for Foo {
    fn into_cow(self) -> Cow<'a, Self> {
        println!("owned");
        Cow::Owned(self)
    }
}

// Doesn't work
impl<'a> Convert<'a> for &'a Foo {
    fn into_cow(self) -> Cow<'a, Self> {
        println!("borrowed");
        Cow::Borrowed(self)
    }
}

The borrowed version says that Borrowed expected a &&'a Foo but found a &'a Foo.

Upvotes: 2

Views: 310

Answers (2)

Solomon Ucko
Solomon Ucko

Reputation: 6109

Do you ever make use of the ownership, or always reborrow it? If only reborrowing, is std::convert::AsRef what you're looking for?

fn take_foo(foo: impl AsRef<Foo>) {
    let foo: &Foo = foo.as_ref();
    todo!();
}

Upvotes: 0

nnnmmm
nnnmmm

Reputation: 8764

Self in the implementation of a trait for &Foo is &Foo, so into_cow() doesn't return the same type in both impls.

One solution would be to change the return type to Cow<'a, Foo>, but that of course limits the trait to only work on Foo.

A better way is to make the owned type a generic parameter of Convert like this:

trait Convert<'a, T: Clone> {
    fn into_cow(self) -> Cow<'a, T>;
}

impl<'a, T: Clone> Convert<'a, T> for T {
    fn into_cow(self) -> Cow<'a, T> {
        println!("owned");
        Cow::Owned(self)
    }
}

impl<'a, T: Clone> Convert<'a, T> for &'a T {
    fn into_cow(self) -> Cow<'a, T> {
        println!("borrowed");
        Cow::Borrowed(self)
    }
}

fn take_foo<'a>(foo: impl Convert<'a, Foo>) {
    let cow = foo.into_cow();
}

fn main() {
    take_foo(&Foo{});
    take_foo(Foo{});
}

Upvotes: 1

Related Questions