Dyn Trait size not known at compile time

I have a program which makes calls to an API. The point is that the programmer can switch between two API's which both provide the same usage but from different providers.

To achieve this I have made a trait:

pub trait GeneralAPI {
   fn func();
   //...
}

Then I have two structs for the two different API providers:

pub struct ApiA;
impl GeneralAPI for ApiA {
    fn func();
    //....
}

pub struct ApiB;
impl GeneralAPI for ApiB {
    fn func();
    //....
}

So the point of this is that the programmer only has to change one line of code to switch the API used:

// Change ApiA to ApiB to switch
let api : ApiA = GeneralAPI::new();
// ...

This works fine. But the problem is that my code is async and uses different threads. So I am already using Arc and Mutex to make this possible:

let api : Arc<Mutex<ApiA>> = Arc::new(Mutex::new(GeneralAPI::new(
    //...
)));

I pass the api to a function called start. It looks like this:

    // Start the trading algorithm. The algorithm keeps running on a seperate thread until it is aborted.
    pub fn start(self, psql: Psql, datastream: Arc<Mutex<mpsc::Receiver<CandleStick>>>, api: Arc<Mutex<ApiA>>) -> tokio::task::JoinHandle<()> {

        // Keep history of incoming data so the algorithm has access to it.
        let mut data : std::vec::Vec<CandleStick> = std::vec::Vec::new();

        // Create seperate thread. In this thread the algorithm is executed each time
        // data is received from the receiver.
        tokio::spawn(async move {
            while let Some(n) = datastream.lock().await.recv().await {    
                data.push(n);
                self.execute(psql.clone(), &data, api.clone()).await;
            }
        })
    }

This works. But the problem I have is the signature of the function start. I have to make the type of the api-parameter Arc<Mutex<ApiA>> instead of Arc<Mutex<dyn GeneralAPI>>. When I use the trait-type I get the error:

error[E0277]: the size for values of type `(dyn GeneralAPI + 'static)` cannot be known at compilation time
   --> src/code.rs:147:51
    |
147 | ...a, api.clone()).await;
    |       ^^^^^^^^^^^ doesn't have a size known at compile-time

I guess this is because dyn GeneralAPI is a dynamically derived type and because it is dynamic it can't know if ApiA or ApiB can be used and thus can't know the size at compile-time. This gives the issue that if I'd wish to change the API-implementation I'd have to change the signature of all my functions to the other struct-type instead of only once in my code (which is bad design).

How would I solve this?

This is a stab in the dark. Can this be fixed via:

pub trait GeneralApi: Sized { ... }

You can clone (std) Arcs regardless of the contained type, even if not Sized.

Can you make the below playground reflect your problem?

Also,

  • what's the full signatures of the execute method?
  • what's the full output of cargo check on the command line?
1 Like

The solution was to add Send to my trait. Thanks!

FYI if you don't want it as a supertrait bound on the trait itself, you can also use

Arc<Mutex<dyn GeneralAPI + Send>>