1

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

1 Answer 1

1
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.

Sign up to request clarification or add additional context in comments.

5 Comments

Thank you so much for your response. So it's not possible to do a kind of factory pattern like in this doc https://doc.rust-lang.org/rust-by-example/trait/dyn.html with associated output ? I thought that using generics over either the struct or the function could do the trick.. Because, what if in this example I want to either return World or World2 based on some input parameters of the functions ?
It is possible, but not if the associated types are different. For example, you can return a Box<dyn Iterator<Item=i32>>. The issue here is that the thing you set Item to must be a concrete type, it can't be a trait.
Actually, your question didn't leave me alone and I kept thinking about it. Then I played around some more and that led me to ask this question. You can have some fancy polymorphism, but only with the right mix of Box<dyn and some mild coercion.
Thanks a lot for this ! This compiler error was also stuck in my head, then I also ask this question in the Rust Forum Community (see users.rust-lang.org/t/…). It seems to be possible also by changing the Output of the implementation to be Box<dyn Hello>. I didn't have the time to test all of this. I try as soon as I can these different solutions.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.