Estou escrevendo um código assíncrono do Tokio. Eu tenho uma estrutura de dados de vários índices que mantém os usuários onde desejo protegê-los com um bloqueio de granulação grossa (ao contrário do bloqueio por objeto). Eu tenho algo assim:
use tokio::sync::RwLock;
struct User {
id: u64,
name: String,
}
// This class is not thread-safe.
struct UserDb {
by_id: HashMap<u64, Arc<RefCell<User>>>,
by_name: HashMap<String, Arc<RefCell<User>>>,
}
impl UserDb {
pub fn add_user(&mut self, name: String) -> Result<(), Error> {
// ...
}
}
// This class is thread-safe.
struct AsyncDb {
users: RwLock<UserDb>,
}
impl AsyncDb {
pub async fn add_user(&self, name: String) -> Result<(), Error> {
self.users.write().await.add_user(name)
}
}
// QUESTION: Are these safe?
unsafe impl Send for AsyncDb {}
unsafe impl Sync for AsyncDb {}
Sem características Send
e Sync
no final, o compilador reclama que RefCell<User>
não é Send
e Sync
(razoavelmente) e, portanto, não é seguro para acessar/modificar por meio de AsyncDb::add_user
.
Minha solução foi implementar Send
e Sync
para a estrutura de dados, uma vez que há um bloqueio de granulação grossa em AsyncDb
torno de UserDb
, que contém os referidos RefCell
s.
Esta é uma solução correta? Isso viola algum invariante? Existe uma maneira melhor de lidar com isso?
Nota: Rust iniciante aqui. Provavelmente tenho muitas lacunas conceituais, então, por favor, informe-as se as coisas não fizerem sentido.