Send and Sync
The problem: sharing data across threads
Section titled “The problem: sharing data across threads”JavaScript sidesteps thread-safety entirely — there is only one thread (per Worker), so the question of “can two threads concurrently access this value?” simply does not arise. There is no type-level concept of thread safety in TypeScript.
Rust supports real parallelism, which means two threads can genuinely access the same memory at the same time. To make this safe without a runtime garbage collector or a lock, Rust uses marker traits: Send and Sync.
Send and Sync defined
Section titled “Send and Sync defined”| Trait | Meaning | Example types |
|---|---|---|
Send | A value of type T can be moved to another thread | i32, String, Vec<T>, Arc<T> |
Sync | A shared reference &T can be sent to another thread | i32, Arc<T>, Mutex<T> |
Not Send | Cannot move to another thread | Rc<T> (non-atomic reference count) |
Not Sync | Cannot share a reference across threads | Cell<T>, RefCell<T> |
These traits are auto-implemented by the compiler based on the types inside your struct. You almost never implement them manually. The rules propagate: if your struct contains a field that is not Send, your struct is not Send either.
// TypeScript has no thread-safety types.// The language guarantees safety by having only one thread per Worker.// There is no type-level concept of Send or Sync.
// In a Worker, you can only communicate via structured-clone (postMessage):// plain objects, arrays, ArrayBuffer, etc.// You CANNOT pass a class instance with methods, a closure, or a WeakRef.
const worker = new Worker('./worker.js');// This WORKS: number is structured-cloneableworker.postMessage(42);// This FAILS at runtime: functions are not cloneable// worker.postMessage(() => 42); // DataCloneError// Rust: Send/Sync are compile-time guarantees, not runtime checks.
use std::sync::{Arc, Mutex};use std::thread;
fn main() { // Arc<Mutex<T>> is both Send and Sync: // - Arc: thread-safe reference counting (vs Rc which is NOT Send) // - Mutex: provides interior mutability with a lock let shared: Arc<Mutex<Vec<u32>>> = Arc::new(Mutex::new(vec![])); let mut handles = vec![];
for i in 0..4 { let data = Arc::clone(&shared); // cheap clone of the Arc pointer let h = thread::spawn(move || { data.lock().unwrap().push(i); // lock, push, auto-unlock }); handles.push(h); }
for h in handles { h.join().unwrap(); }
let mut result = shared.lock().unwrap().clone(); result.sort(); println!("collected: {:?}", result); // collected: [0, 1, 2, 3]}Try it — working version with Arc
Section titled “Try it — working version with Arc”use std::sync::{Arc, Mutex};use std::thread;
fn main() { let data: Arc<Mutex<Vec<u32>>> = Arc::new(Mutex::new(vec![])); let mut handles = vec![];
for i in 0..4 { let d = Arc::clone(&data); let h = thread::spawn(move || { d.lock().unwrap().push(i); }); handles.push(h); }
for h in handles { h.join().unwrap(); }
let mut result = data.lock().unwrap().clone(); result.sort(); println!("collected: {:?}", result);}Compiling…