The issue of safe operation in multithreaded environments has long been faced by programmers, constantly encountering problems such as races, inconsistent data, and memory leaks. Rust has implemented a unique approach with the marker traits Send and Sync to minimize these problems already at the compilation stage.
The problem lies in the lack of access control to shared data between threads, leading to difficult-to-detect errors. In many languages, the responsibility is entirely on the programmer; in Rust, the compiler checks what can be safely passed/shared between threads.
The solution: the Send trait guarantees the safe transfer of an object from one thread to another. Sync allows the shared access to a reference of an object from different threads. Almost all standard types in Rust automatically implement these traits, while custom types can implement them manually or forbid them through impl !Send or impl !Sync for specific cases.
Code example:
use std::sync::{Arc, Mutex}; use std::thread; let counter = Arc::new(Mutex::new(0)); let mut handles = vec![]; for _ in 0..10 { let counter = Arc::clone(&counter); let handle = thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += 1; }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } // counter will always be equal to 10 without races!
Key features:
Can a type with unsafe pointers be Send or Sync?
No, if a type contains a raw pointer or resources without thread safety guarantees, it does not implement these traits, or the developer must implement them manually with full responsibility (usually with unsafe impl Send/Sync).
Are Rc<T> and RefCell<T> Send or Sync?
No, Rc<T> and RefCell<T> are unsafe for concurrent use (neither Send nor Sync). For multithreaded scenarios, Arc<T> and Mutex/RwLock are used.
What happens if a static variable contains a type that does not implement Sync?
Rust will not allow such a static variable to exist: it must be Sync, otherwise the compiler will issue an error.
A young developer places an Rc object in thread::spawn - the code only compiles if Rc is not passed between threads. Attempting to take Rc out of thread::spawn results in a compilation error because Rc does not implement Send and is not protected from races.
Pros:
Cons:
Arc+Mutex is used for a multithreaded counter, all threads work with the same data through a thread-safe interface. No races, the code is safe and robust.
Pros:
Cons: