Course contentsShow
← Rust
Lesson 1132 of 1,91010. Concurrency PrimitivesFree lesson

Demystifying the Send Marker Trait

You will understand which types are safe to transfer across thread boundaries and why.

In this lesson, you will learn how the Send marker trait acts as a compile-time safety guard to ensure data can be safely transferred between threads.

The Thread-Safety Gatekeeper

In Rust’s concurrency model, not every type is safe to move into a different thread. Some types, like those containing non-atomic reference counters (Rc<T>), rely on internal state that would cause data races or memory corruption if accessed simultaneously by multiple threads. To prevent this, Rust uses the Send marker trait.

A type is Send if it is safe to send it to another thread. Because Send is a "marker trait"—meaning it has no methods to implement—it exists solely to tell the compiler: "I guarantee that moving this data across a thread boundary is memory-safe."

How It Works Under the Hood

The Rust compiler automatically implements Send for almost all types, provided that every piece of data inside the type is also Send. For instance, a struct containing only i32 and String fields is automatically Send. However, if you include a type that isn't Send, the entire struct loses its Send status.

Consider this example:

use std::rc::Rc;
use std::thread;

fn main() {
    let data = Rc::new(5);

    // This will cause a compilation error:
    // `Rc<i32>` cannot be sent between threads safely.
    thread::spawn(move || {
        println!("{:?}", data);
    });
}

In the code above, Rc<T> is not Send because it performs non-atomic reference counting. If two threads both owned an Rc pointer to the same heap allocation, they could both try to increment or decrement the reference count simultaneously, leading to a race condition. The compiler detects that the closure moves an Rc into the thread, notices Rc lacks the Send marker, and halts the build.

If you truly need to share data across threads, you must use thread-safe alternatives like Arc<T> (Atomic Reference Counter), which is explicitly marked as Send because it uses atomic operations to update its internal count.

Key Takeaway: The Send trait is a compile-time signal that a type’s internal memory can be safely transferred to another thread without causing data races or undefined behavior.