🦀 Functional Rust
🎬 Fearless Concurrency Threads, Arc>, channels — safe parallelism enforced by the compiler.
📝 Text version (for readers / accessibility)

• std::thread::spawn creates OS threads — closures must be Send + 'static

• Arc> provides shared mutable state across threads safely

• Channels (mpsc) enable message passing — multiple producers, single consumer

• Send and Sync marker traits enforce thread safety at compile time

• Data races are impossible — the type system prevents them before your code runs

460: Send + Sync — Compile-Time Thread Safety Proofs

Difficulty: 3 Level: Intermediate Understand Rust's two marker traits that make "if it compiles, no data races" possible — and learn to read the errors when you violate them.

The Problem This Solves

Every other language with threads relies on runtime checks, discipline, or luck for thread safety. Java has `synchronized` that you can forget. Python has the GIL that hides races until you switch to multiprocessing. Go has a race detector that's opt-in and runtime-only. These approaches catch errors late — in production, under load, on machines you can't inspect. Rust catches thread safety violations at compile time. The mechanism is two marker traits: `Send` and `Sync`. If a type isn't `Send`, the compiler refuses to let you move it to another thread. If a type isn't `Sync`, the compiler refuses to let you share a reference to it across threads. These checks are exhaustive: every type in every crate is checked, including your own. You cannot accidentally forget to apply them. The practical consequence: `thread::spawn` has a bound `F: Send + 'static` on its closure. If you accidentally capture an `Rc<T>` (which is not `Send` because its reference count is not atomic), the compiler tells you immediately with a clear error message, not a race condition observed months later in production.

The Intuition

These are auto-traits: the compiler derives them automatically based on all fields. A struct where every field is `Send` is automatically `Send`. A struct containing an `Rc<T>` is automatically `!Send`. You only implement them manually in `unsafe impl` blocks when you have raw pointers and can prove thread safety yourself (as in example 455).

How It Works in Rust

use std::sync::{Arc, Mutex};
use std::rc::Rc;
use std::thread;

// All fields are Send + Sync → Counter is automatically Send + Sync
struct Counter {
 value: Mutex<u64>,  // Mutex<u64>: Send + Sync
 label: String,      // String: Send + Sync
}

impl Counter {
 fn new(s: &str) -> Self { Counter { value: Mutex::new(0), label: s.to_string() } }
 fn inc(&self) { *self.value.lock().unwrap() += 1; }
 fn get(&self) -> u64 { *self.value.lock().unwrap() }
}

// Use across threads — works because Counter: Send + Sync
let c = Arc::new(Counter::new("test"));
let handles: Vec<_> = (0..4).map(|_| {
 let c = Arc::clone(&c);
 thread::spawn(move || { for _ in 0..100 { c.inc(); } })
}).collect();
for h in handles { h.join().unwrap(); }
println!("{}: {}", c.label, c.get()); // test: 400

// Compile-time proofs — these functions are zero-cost assertions
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}
assert_send::<Counter>();   // compiles → Counter is Send
assert_sync::<Counter>();   // compiles → Counter is Sync

// This would NOT compile:
// struct LocalOnly { _rc: Rc<String> }  // Rc is !Send
// thread::spawn(move || { let _ = LocalOnly { _rc: Rc::new("".into()) }; });
// Error: `Rc<String>` cannot be sent between threads safely
When you see a compile error mentioning `Send` or `Sync`, read it as: "you're trying to cross a thread boundary with a type that isn't safe to cross." The fix is almost always: replace `Rc` with `Arc`, replace `RefCell` with `Mutex`, or restructure to avoid sharing.

What This Unlocks

Key Differences

ConceptOCamlRust
Send typesGC-managed — all values movable by runtime`Arc<T>`, `Mutex<T>`, `String`, `i32` — auto-derived
`!Send` typesN/A (GC handles all)`Rc<T>`, `*mut T`, `RefCell<T>` — not safe to send
Sync typesimmutable values freely shared`Arc<T>`, `Mutex<T>`, `AtomicUsize` — auto-derived
`!Sync` typesN/A`Cell<T>`, `RefCell<T>` — interior mutability without locks
Enforcementruntime + programmer disciplinecompile-time — `Send`/`Sync` violations are errors
Manual implN/A`unsafe impl Send for T {}` — you promise it's safe
// 460. Send + Sync in concurrent code
use std::sync::{Arc, Mutex};
use std::rc::Rc;
use std::thread;

// Auto-derived: all fields are Send+Sync → struct is Send+Sync
struct Counter { value: Mutex<u64>, label: String }
impl Counter {
    fn new(s: &str) -> Self { Counter { value: Mutex::new(0), label: s.to_string() } }
    fn inc(&self) { *self.value.lock().unwrap() += 1; }
    fn get(&self) -> u64 { *self.value.lock().unwrap() }
}

// Contains Rc → NOT Send
struct LocalOnly { _rc: Rc<String> }
// thread::spawn(|| drop(LocalOnly { _rc: Rc::new(...) }))  ← COMPILE ERROR

fn main() {
    let c = Arc::new(Counter::new("test"));
    let hs: Vec<_> = (0..4).map(|_|{let c=Arc::clone(&c); thread::spawn(move || { for _ in 0..100 { c.inc(); } })}).collect();
    for h in hs { h.join().unwrap(); }
    println!("{}: {}", c.label, c.get());  // test: 400

    // Compile-time proofs
    fn assert_send<T: Send>() {}
    fn assert_sync<T: Sync>() {}
    assert_send::<Counter>();
    assert_sync::<Counter>();
    assert_send::<Arc<Counter>>();
    println!("All Send+Sync checks passed at compile time");
}

#[cfg(test)]
mod tests {
    use super::*;
    fn _check_send<T:Send>(){}
    fn _check_sync<T:Sync>(){}
    #[test] fn test_traits() { _check_send::<Counter>(); _check_sync::<Counter>(); }
    #[test] fn test_counter() {
        let c=Arc::new(Counter::new("t"));
        thread::scope(|s|{ for _ in 0..4 { let c=Arc::clone(&c); s.spawn(move ||{ for _ in 0..100 { c.inc(); } }); } });
        assert_eq!(c.get(),400);
    }
}
(* 460. Thread safety concepts – OCaml *)
(* OCaml: GC manages all sharing; safety is by convention, not type system *)

(* "Sync": shared immutable data *)
let shared = [("pi", 3.14159); ("e", 2.71828)]

(* "Mutex-protected": like Arc<Mutex<T>> *)
let count = ref 0
let mutex = Mutex.create ()

let safe_inc () = Mutex.lock mutex; incr count; Mutex.unlock mutex

let () =
  let ts = List.init 4 (fun _ ->
    Thread.create (fun () ->
      (* reading shared is safe (immutable) *)
      let _ = List.assoc "pi" shared in
      for _ = 1 to 100 do safe_inc () done
    ) ()
  ) in
  List.iter Thread.join ts;
  Mutex.lock mutex;
  Printf.printf "count=%d\n" !count;
  Mutex.unlock mutex