Some Name
Some Name

Reputation: 9521

Why isnt Send implemented for a struct containing Arc?

I'm using a crate to interact with Postgres with simply writing sql queries by hands (Diesel seems for my simple case) and got stuck about the multithreaded access to the database client. Here is the code:

use postgres::Client;

pub struct Database{
    connection: Arc<Client>
}

impl Database {
    pub fn from_config(url: &str) -> Database {
        //...
    }
}

fn main() {
    let url: String = //...
    let db = db::Database::from_config(&url);
    let db_ref = Arc::new(db);
    consume(future(Arc::clone(&db_ref))); // <------------------- compile error
}

async fn future(db_ref: Arc<db::Database>){ }

fn consume<F>(f: F)
where F: Send{ }

The postgres::Client is defined as

/// A synchronous PostgreSQL client.
pub struct Client {
    connection: Connection,
    client: tokio_postgres::Client,
}

When compiling this code the compile I got somewhat crazy error message:

error[E0277]: `(dyn futures_core::stream::Stream<Item = std::result::Result<tokio_postgres::AsyncMessage, tokio_postgres::error::Error>> + std::marker::Send + 'static)` cannot be shared between threads safely
  --> src/main.rs:17:5
   |
17 |     consume(future(Arc::clone(&db_ref)));
   |     ^^^^^^^ `(dyn futures_core::stream::Stream<Item = std::result::Result<tokio_postgres::AsyncMessage, tokio_postgres::error::Error>> + std::marker::Send + 'static)` cannot be shared between threads safely
...
24 | fn consume<F>(f: F)
   |    ------- required by a bound in this
25 | where F: Send{ }
   |          ---- required by this bound in `consume`
   |
   = help: the trait `std::marker::Sync` is not implemented for `(dyn futures_core::stream::Stream<Item = std::result::Result<tokio_postgres::AsyncMessage, tokio_postgres::error::Error>> + std::marker::Send + 'static)`
   = note: required because of the requirements on the impl of `std::marker::Sync` for `std::ptr::Unique<(dyn futures_core::stream::Stream<Item = std::result::Result<tokio_postgres::AsyncMessage, tokio_postgres::error::Error>> + std::marker::Send + 'static)>`
   = note: required because it appears within the type `std::boxed::Box<(dyn futures_core::stream::Stream<Item = std::result::Result<tokio_postgres::AsyncMessage, tokio_postgres::error::Error>> + std::marker::Send + 'static)>`
   = note: required because it appears within the type `std::pin::Pin<std::boxed::Box<(dyn futures_core::stream::Stream<Item = std::result::Result<tokio_postgres::AsyncMessage, tokio_postgres::error::Error>> + std::marker::Send + 'static)>>`
   = note: required because it appears within the type `postgres::connection::Connection`
   = note: required because it appears within the type `postgres::client::Client`
   = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<postgres::client::Client>`
   = note: required because it appears within the type `db::Database`
   = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<db::Database>`
   = note: required because it appears within the type `[static generator@src/main.rs:22:43: 22:46 db_ref:std::sync::Arc<db::Database> {}]`
   = note: required because it appears within the type `std::future::from_generator::GenFuture<[static generator@src/main.rs:22:43: 22:46 db_ref:std::sync::Arc<db::Database> {}]>`
   = note: required because it appears within the type `impl core::future::future::Future`
   = note: required because it appears within the type `impl core::future::future::Future`

Which seems to mean that Database does not implement Send. Is there a way to make implement Send? Maybe Mutex or something else should be used instead of Arc?

UPD:

Replacing the struct definition with

pub struct Database{
    connection: Mutex<Client>
}

makes the error disappear, but it is completely unclear why...

Upvotes: 0

Views: 852

Answers (2)

Colonel Thirty Two
Colonel Thirty Two

Reputation: 26569

Arc<T> allows multiple threads to access the same value at the same time. The Sync trait is what verifies that the access will not cause memory unsafety, hence why Arc requires it.

On the other hand, Mutex<T> controls access to T via locking, so that only one thread may access T at a time (in a sense, 'sending' it to the thread that has the lock). So Mutex<T> is Sync even if T is not (though it still must be Send).

However Mutex<T> on its own isn't useful since only one thread will have access to the mutex anyway. You usually combine it with a way of sharing ownership (i.e. Arc) to allow multiple threads to have access the mutex.

Upvotes: 1

Some Name
Some Name

Reputation: 9521

The reason for this was that the trait implementation Send for Arc was defined as

impl<T> Send for Arc<T>
where
    T: Send + Sync + ?Sized, 

So Sync is required to be implemented as well. And that's what the error message was saying. For Mutex in turns Send is defined as

impl<T: ?Sized + Send> Send for Mutex<T>

not requiring Sync to be implemented.

Upvotes: 0

Related Questions