Expected trait in function, want struct in impl

Hi everybody :grinning:

I am confused with traits in Rust (mostly because I came from Java).
I face a problem, and searches show me to use 'Box', but it does not change the error... :confounded:

Here the code :

pub trait Presenter {
    fn success(&mut self, message: dyn OutputMessage);
    fn error(&mut self, message: dyn OutputMessage, error: Box<dyn Error>);
}

pub struct LibraryElementPresenter {
    view_model: Option<LibraryElementViewModel>,
}

impl Presenter for LibraryElementPresenter {
    fn success(&mut self, message: AddLibraryElementOutputMessage) {
        self.view_model = Some(LibraryElementViewModel::new(message.get_library_element, None));
    }

    fn error(&mut self, message: AddLibraryElementOutputMessage, error: Box<dyn Error>) {
        self.view_model = Some(LibraryElementViewModel::new(message.get_library_element, Some(error)));
    }
}

With this code, I get the error : "expected trait object dyn OutputMessage, found struct AddLibraryElementOutputMessage"
In fact, I understand the error, but I don't know how to correct it. Box does not change anything.

And I don't want to use generic, because it could bring complexity in the software architecture that I try to implement (clean architecture).

Have you any idea to correct this please ? :thinking:

Please try to give complete error messages, e.g. as provided by running cargo check in the terminal. Even better would be some self-contained code example, provided as a link to the Rust playground.

If you enclose your code with ```s

```
// your code here
```

instead of just indenting it, you’ll get syntax highlighting in your post.

1 Like

When you implement a trait, the function signatures have to match exactly. These lines should be the same:

fn success(&mut self, message: dyn OutputMessage);
fn success(&mut self, message: AddLibraryElementOutputMessage) {

A Presenter must take any kind of OutputMessage, not just one specific kind. (And you probably don't want dyn OutputMessage in the trait, because it is unsized, so it should be behind a pointer as &dyn OutputMessage or Box<dyn OutputMessage>, etc.)

How to you envision working with a Presenter generically? If you have some generic setting (or a trait object) like

fn some_function<P: Presenter>(presenter: P) {
    // do something with the presenter
}

or

fn some_function(presenter: Box<dyn Presenter>) {
    // do something with the presenter
}

what is the trait supposed to allow you to do inside of these functions? It would allow you to call the trait methods, e.g. presenter.success(…), but can you pass any OutputMessage? Or just an AddLibraryElementOutputMessage? Because your implementation Presenter for LibraryElementPresenter does look like it assumes all it ever gets passed is an AddLibraryElementOutputMessage. But perhaps you want to have other Presenters that expect other kinds of messages.

There really are two possibilities:

  • either LibraryElementPresenter can be re-written to accept any kind of OutputMessage, in which case you could use Box<dyn OutputMessage> (trait objects need indirection such as e.g. boxing).

    in which case you could use fn success(&mut self, message: Box<dyn OutputMessage>) in both the trait declaration and the impl of Presenter.

  • or LibraryElementPresenter really expects an AddLibraryElementOutputMessage, and you aren’t even interested in using the Presenter trait generically. In this case there’s another question

    • do you at least need to be generic over “presenters that use AddLibraryElementOutputMessage as their message type”? If no, then you might not need a trait at all. Just add the methods you want directly to LibraryElementPresenter

    • If you do want this option, (only makes sense if there are multiple types of presenters that use the same AddLibraryElementOutputMessage message type, you can add a type parameter to your trait or an associated type.

      • type parameter

        pub trait Presenter<Message> {
            fn success(&mut self, message: Message);
            fn error(&mut self, message: Message, error: Box<dyn Error>);
        }
        
        pub struct LibraryElementPresenter {
            view_model: Option<LibraryElementViewModel>,
        }
        
        impl Presenter<AddLibraryElementOutputMessage> for LibraryElementPresenter {
            fn success(&mut self, message: AddLibraryElementOutputMessage) {
                self.view_model = Some(LibraryElementViewModel::new(message.get_library_element, None));
            }
        
            fn error(&mut self, message: AddLibraryElementOutputMessage, error: Box<dyn Error>) {
                self.view_model = Some(LibraryElementViewModel::new(message.get_library_element, Some(error)));
            }
        }
        
      • associated type

        pub trait Presenter {
            type Message;
            fn success(&mut self, message: Self::Message);
            fn error(&mut self, message: Self::Message, error: Box<dyn Error>);
        }
        
        pub struct LibraryElementPresenter {
            view_model: Option<LibraryElementViewModel>,
        }
        
        impl Presenter for LibraryElementPresenter {
            type Message = AddLibraryElementOutputMessage;
        
            fn success(&mut self, message: AddLibraryElementOutputMessage) {
                self.view_model = Some(LibraryElementViewModel::new(message.get_library_element, None));
            }
        
            fn error(&mut self, message: AddLibraryElementOutputMessage, error: Box<dyn Error>) {
                self.view_model = Some(LibraryElementViewModel::new(message.get_library_element, Some(error)));
            }
        }
        

      the difference between the two is that the type parameter allows for the same type, e.g. LibraryElementPresenter to have multiple implementations like

      impl Presenter<AddLibraryElementOutputMessage> for LibraryElementPresenter
      

      and also

      impl Presenter<SomeOtherOutputMessageType> for LibraryElementPresenter
      

      while associated types allow only one such implementation (but associated types can be more ergonomic because sometimes it helps that the compiler knows that the associated type depends on the trait implementor).

2 Likes

First of all thanks for yours answers, they help me a lot ! :grin:

Sorry for the topic content, I thought the error message was good enough.
Yeah, for the code syntax, I have forgot the markdown like writing :shushing_face:


As I have already said, I don't want generic in this trait for most reasons (in fact, if they are no others solutions, I will have use it).

Next, for the possibilities that you offered me, I like a lot the second with associated type. I think it matches exactly with what I want to do.
I have not really understand all the things we can do with it when I read the book.

So thanks to your answer, the code now looks like :

pub trait Presenter {
    type OutputMessage;

    fn success(&mut self, message: Self::OutputMessage);
    fn error(&mut self, message: Self::OutputMessage, error: Box<dyn Error>);
}

pub struct LibraryElementPresenter {
    view_model: Option<LibraryElementViewModel>,
}

impl Presenter for LibraryElementPresenter {
    type OutputMessage = AddLibraryElementOutputMessage;

    fn success(&mut self, message: AddLibraryElementOutputMessage) {
        self.view_model = Some(LibraryElementViewModel::new(message.get_library_element(), None));
    }

    fn error(&mut self, message: AddLibraryElementOutputMessage, error: Box<dyn Error>) {
        self.view_model = Some(LibraryElementViewModel::new(message.get_library_element(), Some(error)));
    }
}

With ViewModel and OutputMessage structs that don't impact the comprehension of the code.

For comprehension, the trait presenter is generic, and the struct that implement Presenter are specific to an OutputMessage. If we want to use another OutputMessage, we have to create a new presenter for it.

(maybe I should have explain this in my question ? :sweat_smile: :zipper_mouth_face:)

I don't know if it is really comprehensive :sweat_smile:

By the way, if you have some useful functionality in an OutputMessage trait and ever want to be generic over Presenters you can consider adding a trait bound on the associated type. That would look like

// defined elsewhere:
pub trait OutputMessage { … }

////////////////////////////////////////////////////////////////////

pub trait Presenter {
    // this looks a bit confusing since both the associated
    // type and a trait are called `OutputMessage`
    //
    //         +----------------------- name of the associated type
    //         |
    //   vvvvvvvvvvvvv  vvvvvvvvvvvvv-- the `OutputMessage` trait
    type OutputMessage: OutputMessage;

    fn success(&mut self, message: Self::OutputMessage);
    fn error(&mut self, message: Self::OutputMessage, error: Box<dyn Error>);
}

Yup, you are right, it is less confusing like that.
I have corrected this in my code

Thanks :slight_smile: