Вы можете использовать систему типов, чтобы обернуть Arc<Mutex<T>>
способом, который запрещает мутацию, кроме одного привилегированного владельца.Вот пример:
use std::sync::Arc;
use std::sync::Mutex;
pub struct Writer<T>(Arc<Mutex<T>>);
impl<T> Writer<T> {
pub fn new(value: T) -> Self {
Writer(Arc::new(Mutex::new(value)))
}
pub fn reader(&self) -> Reader<T> {
Reader(Arc::clone(&self.0))
}
pub fn set(&self, value: T) {
*self.0.lock().unwrap() = value;
}
pub fn get(&self) -> T
where
T: Clone,
{
self.0.lock().unwrap().clone()
}
}
pub struct Reader<T>(Arc<Mutex<T>>);
// derive(Clone) uses incorrect bounds, so we must implement Clone manually
// (see https://stackoverflow.com/q/39415052/3650362)
impl<T> Clone for Reader<T> {
fn clone(&self) -> Self {
Reader(Arc::clone(&self.0))
}
}
impl<T> Reader<T> {
pub fn get(&self) -> T
where
T: Clone,
{
self.0.lock().unwrap().clone()
}
}
Если вы поместите этот код в mod
файл, управление конфиденциальностью Rust докажет, что ни один пользователь не может дублировать Writer
или превратить Reader
в Writer
кроме как с помощью unsafe
.Поэтому вы можете клонировать и отправлять Reader
s в любое количество потоков, но отправлять Writer
только в конкретный поток, который должен иметь доступ для записи.
Существует множество возможных вариантов этого дизайна;например, вы можете использовать RwLock
вместо Mutex
, чтобы позволить нескольким читателям одновременно получать доступ к значению, пока оно не записывается.
Playground (на примере Akiner Alkan)
Нечто подобное можно было бы сказать, например, C
Обратите внимание, что, как и в Rust, если вы хотите сделать безопасно в C, вынужна какая-то синхронизация (мьютекс или аналогичный).Rust настаивает на том, чтобы вы четко указывали, как избежать гонок данных.С отличается тем, что он просто предполагает, что вы знаете, что вы делаете, а затем жестоко накажет вас за написание гонок.В Rust идиоматический подход заключается в использовании безопасных абстракций, предоставляемых стандартной библиотекой.Однако, если у вас есть какие-то другие средства синхронизации и вы можете доказать, что Mutex
- ненужные накладные расходы, вы всегда можете просто написать что-то по-C - необработанные указатели по сути одинаковы в обоих Rust (в пределах блока unsafe
)и С.