`expected type parameter, found struct` when trying to use trait object with tokio::sync::mpsc::sender

I'm trying to send a trait object through a tokio::sync::mpsc::sender

use tokio::sync::mpsc;

trait SomeTrait {
}

struct Foo{
}

impl SomeTrait for Foo{
}

async fn bar(mut sender: mpsc::sender<(impl SomeTrait, String)>){
    if let Some(foo, some_string) = baz().await{
        let _ = sender.send((foo,some_string));
                             ^^^ error occurs here
    }
}

async fn baz() -> Option<(Foo,String)> {
     //returns desired type
}

this results in this error

mismatched types
expected type parameter `impl SomeTrait`
           found struct `Foo`

I feel like I'm doing something obvious wrong? I've been using trait objects all the time, not exactly sure what's different here.

fyi I don't think it has anything to do with the Tuple and the string, tried without them, same issue

When you use impl SomeTrait in the type of a function parameter, the caller of the function gets to choose the type. In other words, it is shorthand for:

async fn bar<T>(mut sender: mpsc::Sender<(T, String)>) where T: SomeTrait

However, your function body expects a Sender<(Foo, String)>, and won't work with any other type of Sender. So it will only compile if you require the caller to pass a Sender<(Foo, String)>:

async fn bar(mut sender: mpsc::Sender<(Foo, String)>)

So isn't there any way to define it the other way around, as in bar requires a Sender<(SomeTrait, String)> rather than letting the caller decide?

If SomeTrait were implemented for i32, and someone sent in an mpsc::Sender<(i32, String)>, your function would not be able to handle it, because the sender is given a (Foo, String) instead. Therefore this doesn't work.

Note, impl Trait doesn't give you trait objects. You may be looking for dyn Trait instead. Trait objects are unsized, so they need to be passed behind some form of pointer. Generally a Box is a good choice. So you could try:

async fn bar(mut sender: mpsc::sender<(Box<dyn SomeTrait>, String)>){
    if let Some(foo, some_string) = baz().await{
        let _ = sender.send((Box::new(foo), some_string));
    }
}
1 Like

Alright I guess I'll have to go for Box then. I knew that would probably work, but didn't want to use it because that impl Trait is used all over my code. I'll have to go around changing all of them now :sweat_smile: