Why isnt Send implemented for a struct containing Arc?

こ雲淡風輕ζ 提交于 2020-08-10 20:13:09

问题


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...


回答1:


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.




回答2:


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.



来源:https://stackoverflow.com/questions/63144311/why-isnt-send-implemented-for-a-struct-containing-arc

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!