Historically, multithreading has been accompanied by crashes, races, and leaks, especially with uncontrolled memory sharing. Rust implements the concept of thread safety at the type level — an object can only be transferred to a thread if it implements the necessary traits (Send, Sync). Threads are created via std::thread::spawn, and communication between them is conducted through channels or shared memory with controlled mutation (Mutex, Arc).
Problem: Managing synchronization manually is complex and dangerous. Transferring arbitrary objects between threads without explicit ownership transfer leads to races and crashes.
Solution: Only explicitly movable (move) objects or shared through Arc, Mutex, as well as built-in message channels (std::sync::mpsc, crossbeam). This minimizes errors associated with synchronous and asynchronous data exchange: ownership is always unambiguous.
Code example:
use std::thread; use std::sync::mpsc; fn main() { let (tx, rx) = mpsc::channel(); thread::spawn(move || { tx.send(String::from("Hello from thread!")).unwrap(); }); let received = rx.recv().unwrap(); println!("Received: {}", received); }
Key features:
Can you continue using an object in the main thread after transferring it via move?
No, once the object is moved (e.g., into a closure in thread::spawn), it cannot be used in the parent thread; the compiler will not allow the code to compile.
Can mutable references (&mut T) be passed between threads?
No, a mutable reference &mut T can exist only in one instance, and the Send trait is not implemented for it by default. To work with mutable data, a wrapper through Mutex/Arc is used.
Why can't Rc<T> be used to share ownership between threads?
Rc<T> does not implement Sync and Send as its internal counter is not thread-safe. For thread-safe sharing, Arc<T> (atomic reference counter) is used.
// Comparison of Rc and Arc use std::sync::Arc; let x = Arc::new(5); // can be cloned and shared among threads
A developer decided to share a string between threads using Rc<String> and puts Rc inside thread::spawn. The code compiles only if force-casted via unsafe, after which the application can crash or work with corrupted data.
Pros:
Cons:
Arc<String> + Mutex<String> is used for safe access, or message is sent through a channel without shared ownership.
Pros:
Cons: