Search code examples
rustasync-awaitrust-tokio

Sharing an immutable reference to a Send resource in Rust


I am trying to get some async Rust going, to allow concurrent access to a database. I understand futures need to have 'Send' implemented for all the variables that can be held whilst it is awaited, but I am specifically importing Send for the Database itself, as well as declaring it on the input type.

#[async_trait]
pub trait Database: Send {
    async fn get_something(&self) -> Option<Vec<f32>>;
}

async fn calculate_something(&mut self, db: Arc<(dyn Database + Send)>, var: i16) -> Option<f32> {
    let something = db.get_something(var).await;
    // Do stuff with the something.

    Some()
}

This gives me the error:

^ future created by async block is not `Send`
help: the trait `Sync` is not implemented for `dyn database::Database + Send`
note: captured value is not `Send`
  --> src/alarms/something_calculator.rs:31:47
   |
31 |     async fn calculate_something(&mut self, db: Arc<(dyn Database + Send)>,     var: i16) -> Option<f32> {
   |                                               ^^ has type `Arc<dyn database::Database + Send>` which is not `Send`
   = note: required for the cast to the object type `dyn Future<Output = Option<f32>> + Send`

I am new to rust so I'm sure I am doing something silly, but I can't wrap my head around it not implementing Send when it is defined as a Send in its type.

Thanks in advance.


Solution

  • The async function requires all captured values to be Send such that itself is Send. For the Arc type to be Send its type argument must be Sync + Send. You can write db: Arc<(dyn Database + Send + Sync)> to make the async Future send.

    It mentions this in the error message:

    help: the trait `Sync` is not implemented for `dyn database::Database + Send
    

    This change might have further consequences for your code if the db argument is not already Sync, but you are not providing enough context to determine that.