简体   繁体   中英

Combine traits with associated types in rust

I'm trying to create a kind of wrapper trait that combines multiples traits and a function that return the associated implementation of the trait. It works well as long as you don't have associated types. I don't know how to refer to the Output type I am aware of how to combine traits: Is there a way to combine multiple traits in order to define a new trait? Is there any way to create a type alias for multiple traits?

But unfortunately, I've not found anything with associated types

Here is a summarized example of where I'm stucked

use std::ops::Add;

pub trait Hello {
    fn hello(&self);
}
pub trait Goodbye {
    fn goodbye(&self);
}
struct Suffix {
    suffix: String,
}
pub struct World {
    content: String,
}

impl World {
    fn new(content: String) -> Self {
        World { content: content }
    }
}

impl Hello for World {
    fn hello(&self) {
        println!("Hello {}", self.content)
    }
}
impl Goodbye for World {
    fn goodbye(&self) {
        println!("Goodbye {}", self.content)
    }
}
impl Add<Suffix> for World {
    type Output = World;
    fn add(self, other: Suffix) -> World {
        let suffixed: String = self.content + &other.suffix;
        World::new(suffixed)
    }
}

trait HelloGoodbye: Hello + Goodbye {}
impl<T> HelloGoodbye for T where T: Hello + Goodbye {}

fn get_hello_goodbye() -> Box<dyn HelloGoodbye> {
    Box::new(World::new("Everyone".to_string()))
}

trait SuffixableHello: Hello + Add<Suffix, Output = Self> {}
impl<T> SuffixableHello for T where T: Hello + Add<Suffix, Output = Self> {}

fn get_suffixable_hello() -> Box<dyn SuffixableHello> {
    Box::new(World::new("Everyone".to_string()))
}


fn main() {
    // This works
    let hello_goodbye = get_hello_goodbye();
    hello_goodbye.hello();
    hello_goodbye.goodbye();

    // This does not work
    let suffixable_hello = get_suffixable_hello();
    suffixable_hello.hello()
}

I got this compilation error:

49 | fn get_suffixable_hello() -> Box<dyn SuffixableHello> {
   |                                      ^^^^^^^^^^^^^^^ help: specify the associated type: `SuffixableHello<Output = Type>`

What am I supposed to put there?

What I've try so far:

  • Make trait generic
trait SuffixableHello<T>: Hello + Add<Suffix, Output = T> {}
impl<T, U> SuffixableHello<T> for T where T: Hello + Add<Suffix, Output = Self> {}

And I get

   |
49 | fn get_suffixable_hello() -> Box<dyn SuffixableHello<T>> {
   |                                      ~~~~~~~~~~~~~~~~~~

Where am I supposed to add this T generic? Does my fucntion need to be generic?

  • Only add Output to implementation
trait SuffixableHello: Hello + Add<Suffix> {}
impl<T> SuffixableHello for T where T: Hello + Add<Suffix, Output = T> {}

But I get:

the value of the associated type `Output` (from trait `Add`) must be specified

This makes sense since Output is not declared in the trait.

  • Replacing World by Self in the impl Add<Suffix> for World

Am I missing something here? Thank you

Also, what if we want to return from the get_suffixable_hello() one of two Hello implementations, let's say World and World2 to cite @cadolphs like in this doc https://doc.rust-lang.org/rust-by-example/trait/dyn.html

fn get_suffixable_hello() -> Box<dyn SuffixableHello<Output=World>> {
    Box::new(World::new("Everyone".to_string()))
}

does the trick.

EDIT: Longer explanation. Your first initial compiler error tells you that you can't just return Box<dyn SuffixableHello> . That's because having the same trait but with different associated types isn't allowed, because the signatures of associated methods would be different.

So. We need to put a type there. And what should that type be? Well, given that you're explicitly calling World::new , there's really only one type that makes sense here, and that's World .

In your toy example that makes it of course a bit silly and redundant because there's only one struct that implements all those traits.

If you had another struct, World2 , you could not have it be returned by get_suffixable_hello next to World , because World2 would have associated type Output=World2 and hence wouldn't match the Output=World associated type.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM