pub struct SyncCell<T> { /* private fields */ }Expand description
A runtime-checked cell that allows sharing non-Sync types between threads.
SyncCell<T> wraps a value of type T (which may not implement Sync) and provides
a Sync implementation using a mutex for thread-safe access. Access to the wrapped
value is provided through closure-based methods that automatically manage the mutex.
Unlike crate::unsafe_sync_cell::UnsafeSyncCell, this provides memory safety by using a real mutex
and proper synchronization. The closure-based API prevents common issues like
holding guards across await points or forgetting to release locks.
§Examples
Basic usage with a non-Sync type:
use send_cells::SyncCell;
use std::cell::RefCell;
use std::sync::Arc;
use std::thread;
// RefCell<i32> is not Sync, but SyncCell<RefCell<i32>> is
let data = RefCell::new(42);
let cell = Arc::new(SyncCell::new(data));
let cell_clone = Arc::clone(&cell);
let handle = thread::spawn(move || {
cell_clone.with(|ref_cell| {
assert_eq!(*ref_cell.borrow(), 42);
});
});
handle.join().unwrap();Mutable access:
use send_cells::SyncCell;
use std::collections::HashMap;
let map = HashMap::new();
let cell = SyncCell::new(map);
cell.with_mut(|map| {
map.insert("key", "value");
});
cell.with(|map| {
assert_eq!(map.get("key"), Some(&"value"));
});§Thread Safety
The cell implements both Send and Sync when the wrapped type implements Send.
Access is always protected by the internal mutex, ensuring thread safety.
Implementations§
Source§impl<T> SyncCell<T>
impl<T> SyncCell<T>
Sourcepub fn new(value: T) -> SyncCell<T>
pub fn new(value: T) -> SyncCell<T>
Creates a new SyncCell wrapping the given value.
The value will be protected by an internal mutex, allowing safe shared access from multiple threads through the closure-based access methods.
§Examples
use send_cells::SyncCell;
use std::rc::Rc;
let data = Rc::new("Hello, world!");
let cell = SyncCell::new(data);
cell.with(|rc| {
println!("{}", rc);
});Sourcepub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R
pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R
Accesses the underlying value through a synchronous closure.
The closure receives a shared reference to the wrapped value and must return synchronously. The internal mutex is automatically acquired before calling the closure and released when the closure returns.
This method provides safe, synchronized access to the wrapped value from any thread.
§Panics
Panics if the mutex is poisoned (i.e., another thread panicked while holding the lock).
§Examples
use send_cells::SyncCell;
use std::collections::HashMap;
let mut map = HashMap::new();
map.insert("key", "value");
let cell = SyncCell::new(map);
let result = cell.with(|map| {
map.get("key").copied()
});
assert_eq!(result, Some("value"));Sourcepub fn with_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> R
pub fn with_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> R
Accesses the underlying value mutably through a synchronous closure.
The closure receives a mutable reference to the wrapped value and must return synchronously. The internal mutex is automatically acquired before calling the closure and released when the closure returns.
This method provides safe, synchronized mutable access to the wrapped value from any thread.
§Panics
Panics if the mutex is poisoned (i.e., another thread panicked while holding the lock).
§Examples
use send_cells::SyncCell;
use std::collections::HashMap;
let map = HashMap::new();
let cell = SyncCell::new(map);
cell.with_mut(|map| {
map.insert("key", "value");
});
cell.with(|map| {
assert_eq!(map.get("key"), Some(&"value"));
});Sourcepub fn into_inner(self) -> T
pub fn into_inner(self) -> T
Consumes the cell and returns the wrapped value.
This method takes ownership of the SyncCell and returns the wrapped value
without any synchronization, since the cell is being consumed.
§Examples
use send_cells::SyncCell;
use std::rc::Rc;
let data = Rc::new("Hello, world!");
let cell = SyncCell::new(data);
let recovered_data = cell.into_inner();
assert_eq!(*recovered_data, "Hello, world!");Sourcepub unsafe fn with_unchecked(&self) -> &T
pub unsafe fn with_unchecked(&self) -> &T
Unsafely accesses the underlying value without acquiring the mutex.
§Safety
The caller must ensure that:
- No other thread is currently accessing the value
- The access is properly synchronized through external means
- The mutex is not poisoned
This method bypasses all synchronization and may lead to data races if used incorrectly.
§Examples
use send_cells::SyncCell;
let cell = SyncCell::new(42);
// SAFETY: We're the only thread accessing this cell
let value = unsafe { cell.with_unchecked() };
assert_eq!(*value, 42);Sourcepub unsafe fn with_mut_unchecked(&self) -> &mut T
pub unsafe fn with_mut_unchecked(&self) -> &mut T
Unsafely accesses the underlying value mutably without acquiring the mutex.
§Safety
The caller must ensure that:
- No other thread is currently accessing the value
- The access is properly synchronized through external means
- The mutex is not poisoned
- No other references (mutable or immutable) to the value exist
This method bypasses all synchronization and may lead to data races if used incorrectly.
§Examples
use send_cells::SyncCell;
let cell = SyncCell::new(42);
// SAFETY: We're the only thread accessing this cell
unsafe {
*cell.with_mut_unchecked() = 100;
}
cell.with(|value| {
assert_eq!(*value, 100);
});