Under The Hood
backend rust Rust 1.x

Thread Safety: Auto-Traits Send and Sync Compile-Time Verification

Last updated
Prerequisites:
Rust ownership and borrowing
Understanding of threads and shared state
  • rust
  • send
  • sync
  • auto-traits
  • thread-safety
  • concurrency

Read at your depth

The practical view

Send and Sync are marker traits (auto-traits) that the compiler derives automatically. Send: a type can be moved to another thread safely (ownership transfer). Sync: a type can be shared by reference across threads (&T is Send). Most types are Send + Sync. Exceptions: Rc<T> is neither (not thread-safe); RefCell<T> and Cell<T> are Send but not Sync (single-threaded interior mutability); *const T/*mut T raw pointers are not Send/Sync by default (unsafe). Send/Sync are enforced at compile time: you cannot move a !Send value to another thread or share a !Sync value. You can implement them manually with unsafe impl for types you control (with responsibility for correctness).

The same idea in other frameworks

react equivalent
N/A — Rust language feature.

Legacy vs modern

Sharing Rc across threads (compile error) vs Arc

Rc's non-atomic refcount makes it !Send/!Sync — a compile error; Arc uses atomic refcounting and is shareable.

before → after
Rc moved into a thread
let rc = Rc::new(5);
thread::spawn(move || { let _ = *rc; }); // ❌ OLD: compile error — Rc is !Send
Arc for cross-thread sharing
let arc = Arc::new(5); // ✅ NEW: atomic refcount, Send + Sync
let h = thread::spawn(move || { let _ = *arc; }); // ✅ NEW: compiles

Interview gotchas

Context

This tests the two auto-trait semantics — interviewers want the move-vs-share distinction and the precise relationship (Sync ⇔ &T is Send), not just 'thread-safe markers'.

The mechanical answer

Send means a value can be safely MOVED to another thread — ownership is transferred, and the receiver can use it exclusively. Sync means a value can be safely SHARED across threads — &T can be sent to another thread and read concurrently. The precise relationship: T: Sync is equivalent to &T: Send (if a shared reference to T can be sent, T is Sync). A type can be Send but not Sync (e.g. RefCell<T>: you can move it to another thread, but you cannot share &RefCell across threads because its borrow flag is non-atomic). A type can be neither (Rc). The guarantees are compile-time: thread::spawn requires the closure (including captured values) to be Send; sharing across threads requires the value to be Sync (or wrapped in a lock/Arc). These are the compile-time translation of 'no data races'.

Trap

The naive answer is 'Send and Sync are the same thing'. Send = move across threads; Sync = share across threads, and the &T: Send equivalence is the precise relationship. Another trap: assuming all standard types are Send+Sync — Rc, RefCell, raw pointers, and *const T are not, by design. Candidates who state the &T: Send equivalence show real understanding.

Under The Hood — a multi-depth technical interview hub.

Third-party licenses Press ⌘ K to search.