MaybeOwned implementation generic over ToOwned

34 views Asked by At

I have the following trait:

/// A trait for accepting values which may or may not be owned.
trait MaybeOwned<B: ToOwned + ?Sized> {
    fn is_owned(&self) -> bool;
    fn into_owned(self) -> <B as ToOwned>::Owned;
    fn borrow(&self) -> &B;
}

An example for how I'd like it to behave:

fn mo_u32<T: MaybeOwned<u32>>(x: T) { println!("{} {}", x.is_owned(), x.borrow()); }
fn mo_str<T: MaybeOwned<str>>(x: T) { println!("{} {}", x.is_owned(), x.borrow()); }

fn main() {
    mo_u32(42);                  // true 42    
    mo_u32(&42);                 // false 42   
    mo_str("test");              // false test 
    mo_str("test".to_string());  // true test  
    mo_str(&"test".to_string()); // false test 
}

The purpose of it so that a function can be generic over owned and borrowed values, to reduce the amount of unnecessary clones in our codebase (the types in question are large containers of data). It is also possible to do this by accepting Cow arguments, but this (depending on inlining) pushes the decision to runtime rather than compile time, and adds extra noise for callers.

I was wondering, is it possible to implement this trait completely generically over ToOwned in stable Rust?

I have come up with an implementation that works, but is not generic over ToOwned. If you want MaybeOwned<str> to accept Strings you need a separate implementation for that. This implementation is listed below, along with two examples for String <-> str and Vec<T> <-> [T] as well as Cow.

impl<T: ToOwned<Owned=T>> MaybeOwned<T> for T {
    fn is_owned(&self) -> bool { true }
    fn into_owned(self) -> T { self }
    fn borrow(&self) -> &T { self }
}

impl<B: ToOwned + ?Sized> MaybeOwned<B> for &B {
    fn is_owned(&self) -> bool { false }
    fn into_owned(self) -> <B as ToOwned>::Owned { self.to_owned() }
    fn borrow(&self) -> &B { self }
}

impl<B: ToOwned + ?Sized> MaybeOwned<B> for Cow<'_, B> {
    fn is_owned(&self) -> bool { 
        match *self {
            Cow::Borrowed(_) => true,
            Cow::Owned(_) => false,
        }
    }
    fn into_owned(self) -> <B as ToOwned>::Owned { Cow::into_owned(self) }
    fn borrow(&self) -> &B { Borrow::borrow(self) }
}

impl MaybeOwned<str> for String {
    fn is_owned(&self) -> bool { true }
    fn into_owned(self) -> String { self }
    fn borrow(&self) -> &str { self }
}

impl MaybeOwned<str> for &String {
    fn is_owned(&self) -> bool { false }
    fn into_owned(self) -> String { self.clone() }
    fn borrow(&self) -> &str { self }
}

impl<T: Clone> MaybeOwned<[T]> for Vec<T> {
    fn is_owned(&self) -> bool { true }
    fn into_owned(self) -> Vec<T> { self }
    fn borrow(&self) -> &[T] { self }
}

impl<T: Clone> MaybeOwned<[T]> for &Vec<T> {
    fn is_owned(&self) -> bool { false }
    fn into_owned(self) -> Vec<T> { self.clone() }
    fn borrow(&self) -> &[T] { self }
}
0

There are 0 answers