In this lesson, you will learn how Rust uses the Send and Sync marker traits to prevent data races at compile time, ensuring memory safety in multi-threaded code.
The Problem: Data Races
A data race occurs when two threads access the same memory location simultaneously, and at least one of those accesses is a write. In many languages, this leads to undefined behavior that is notoriously difficult to debug. Rust solves this by leveraging its type system to track which data is safe to share across thread boundaries.
The Marker Traits: Send and Sync
These are "marker traits," meaning they have no methods to implement. They simply tell the compiler about a type's capabilities:
Send: A type isSendif it is safe to transfer ownership of it between threads. If you move aSendtype from thread A to thread B, the compiler ensures thread A can no longer access it.Sync: A type isSyncif it is safe to access it from multiple threads simultaneously (i.e., it is safe to share references&Tbetween threads).
How Rust Enforces Safety
Most primitive types in Rust (like i32 or bool) are both Send and Sync. However, types that manage their own internal memory without synchronization—like Rc<T>—are explicitly not Sync. If you try to share an Rc<T> across threads, the compiler stops you:
use std::rc::Rc;
use std::thread;
fn main() {
let data = Rc::new(5);
// This code will fail to compile!
thread::spawn(move || {
println!("{:?}", data);
});
}
Because Rc<T> uses a non-atomic reference counter, it is not thread-safe. If two threads tried to update the count at the same time, the memory could be corrupted. Rust identifies this through the Send trait bound required by thread::spawn. By refusing to compile, Rust turns a potential runtime crash into a static guarantee.
When you need multi-threaded shared access, you move from Rc<T> to Arc<T> (Atomic Reference Counting), which is explicitly marked as Sync, signaling to the compiler that its internal operations are atomic and safe for concurrent use.
Key Takeaway: The Send and Sync traits act as compile-time guardrails, ensuring that only data structures explicitly designed for concurrent access can be shared between threads.