Return only an owned type from a trait method that can accept an owned or borrowed value as input

Viewed 94

I want to have a trait that can be implemented for T and &T but has methods that always return T.

What I would like to do is something like this

use std::borrow::ToOwned;

trait Foo<X: ToOwned> {
    fn f(&self, x: X) -> f64;
    
    fn g(&self) -> X::Owned;
}

struct Float(f64);

impl Foo<f64> for Float {
    fn f(&self, x: f64) -> f64 {
        x + self.0
    }
    
    fn g(&self) -> f64 {
        self.0 * 2.0
    }
}

struct List(Vec<f64>);

impl Foo<&Vec<f64>> for List {
    fn f(&self, x: &Vec<f64>) -> f64 {
        x.iter().sum()
    }
    
    // Error here - `&Vec<f64>` return type expected
    fn g(&self) -> Vec<f64> {
        self.0.iter().map(|&x| 2.0 * x).collect()
    }
}

fn main() {
    let float = Float(2.0);
    println!("{} {}", float.f(3.0), float.g());
   
    let list = List(vec![0.0, 1.0, 2.0]);
    println!("{} {:?}", list.f(&vec![1.0, 2.0]), list.g());
}

I know that one option is to have a trait that defines the output type like so

trait FooReturn {
   type Output;
}

trait Foo<X: FooReturn> {
    fn f(&self, x: X) -> f64;

    fn g(&self) -> X::Output;
}

then implement the trait for all relevant types, but I was wondering if there was a more standard/robust way to do this.

1 Answers

This is how you would do it once specialization is complete. Meanwhile, I couldn't even get a simple working example to compile on 1.55.0-nightly.

#![feature(specialization)]

trait MaybeOwned {
    type Owned;
}

default impl<X> MaybeOwned for X {
    type Owned = X;
}

impl<'a, X> MaybeOwned for &'a X {
    type Owned = X;
}

trait Foo<X: MaybeOwned> {
    fn f(&self, x: &X) -> f64;
    fn g(&self) -> <X as MaybeOwned>::Owned;
}
Related