send_cells
Thread-safe cell types for sending and sharing non-Send/non-Sync types across thread boundaries.

This crate provides specialized cell types that allow you to work with types that don't normally
implement Send or Sync traits, enabling their use in concurrent contexts while maintaining
memory safety through either runtime checks or manual verification.
Overview
The send_cells crate offers two categories of wrappers:
- Safe wrappers with runtime thread checking
- Unsafe wrappers for performance-critical scenarios with manual safety verification
This crate may be considered an alternative to the fragile crate, but provides a more ergonomic API and additional unsafe variants for maximum performance.
Quick Start
use ;
use Rc;
use Arc;
use thread;
// Wrap a non-Send type to make it Send
let data = new;
let send_cell = new;
// Access is checked at runtime - panics if accessed from wrong thread
assert_eq!;
// Wrap a non-Sync type to make it Sync
let shared_data = new;
let sync_cell = new;
// Share between threads with automatic synchronization
let sync_clone = clone;
spawn.join.unwrap;
Safe Wrappers
Safe wrappers provide runtime-checked access to wrapped values:
SendCell<T>
Allows sending non-Send types between threads with runtime thread checking:
- Remembers the thread it was created on
- Panics if accessed from a different thread
- Perfect for single-threaded async contexts
SyncCell<T>
Allows sharing non-Sync types between threads with mutex-based synchronization:
- Uses internal mutex for thread-safe access
- Closure-based API prevents holding locks across await points
- Ideal for shared state in multi-threaded applications
SendFuture<T>
Wraps non-Send futures to make them Send:
- Runtime checks ensure the future is only polled on the correct thread
- Enables use of non-Send futures with thread pool executors
Unsafe Wrappers
Unsafe wrappers provide zero-cost abstractions when you can manually verify safety:
UnsafeSendCell<T>
Allows sending non-Send types without runtime checks:
- No performance overhead
- Requires
unsafeblocks for all access - Suitable for platform-specific thread guarantees
UnsafeSendFuture<T>
Wraps non-Send futures without runtime checks:
- Zero overhead compared to the underlying future
- Requires manual verification of thread safety
UnsafeSyncCell<T>
Allows sharing non-Sync types without runtime checks:
- No synchronization overhead
- Requires
unsafeblocks for all access - Suitable when external synchronization is guaranteed
When to Use Each Type
| Type | Use When | Performance | Safety |
|---|---|---|---|
SendCell |
Moving non-Send types in async contexts | Good | Runtime checked |
SyncCell |
Sharing non-Sync types between threads | Good | Mutex protected |
SendFuture |
Using non-Send futures with Send requirements | Good | Runtime checked |
UnsafeSendCell |
Platform guarantees thread safety | Best | Manual verification |
UnsafeSyncCell |
External synchronization guarantees | Best | Manual verification |
UnsafeSendFuture |
Maximum performance for futures | Best | Manual verification |
Platform Support
Standard Platforms
Full support for all major platforms with standard library support.
WebAssembly
This crate has full wasm32-unknown-unknown support with runtime thread checks
for web workers. Thread IDs are properly tracked even in WASM environments.
Examples
Async Runtime Integration
use send_cells::SendCell;
use std::rc::Rc;
async fn process_data() {
// Rc is not Send, but we need to use it in an async context
let data = Rc::new(vec![1, 2, 3]);
let cell = SendCell::new(data);
// Can be moved into async blocks that might run on different threads
// Note: This would panic if actually polled on a different thread!
let task = async move {
// Will panic if actually polled on a different thread
let data = cell.get();
data.iter().sum::<i32>()
};
// In a real application with tokio:
// let result = tokio::spawn(task).await.unwrap();
}
Shared State with SyncCell
use SyncCell;
use RefCell;
use Arc;
use thread;
let counter = new;
let sync_counter = new;
let mut handles = vec!;
for _ in 0..10
for handle in handles
sync_counter.with;
Platform-Specific Usage
use UnsafeSendCell;
use Rc;
// Platform API guarantees callbacks run on main thread
#
Safety Considerations
Safe Wrappers
The safe wrappers (SendCell, SyncCell, SendFuture) provide memory safety through:
- Runtime thread checking with clear panic messages
- Automatic synchronization via mutexes
- Prevention of common concurrency bugs
Unsafe Wrappers
The unsafe wrappers require manual verification of:
- Thread-local state dependencies
- Concurrent access patterns
- Drop safety on different threads
- External synchronization requirements
Always prefer safe wrappers unless you have specific performance requirements and can rigorously verify thread safety.
Performance
Runtime Overhead
- Safe wrappers: Small overhead for thread ID checking or mutex operations
- Unsafe wrappers: Zero runtime overhead
Memory Overhead
- SendCell: One
ThreadId+ wrapped value - SyncCell: One
Mutex<()>+ wrapped value - UnsafeSendCell: No overhead (transparent wrapper)
Related Crates
- fragile - Similar functionality with different API design
- once_cell - Lazy initialization primitives
- parking_lot - Alternative synchronization primitives