Rust Marker Traits: Properties as Types
Marker traits are empty labels telling the Rust compiler about a type's capabilities, like being copyable or thread-safe. They have no methods; their presence is the signal. They're key for concurrency (`Send`/`Sync`) and memory (`Copy`/`Sized`) safety checks.
WHY IT EXISTS Rust needs a way to enforce compile-time guarantees about how types behave, especially concerning memory and concurrency. Instead of special-casing these rules in the compiler, Rust encodes them as traits that types can implement, making the system extensible and explicit.
THE MENTAL MODEL Think of marker traits as tags you attach to a shipping box. A "FRAGILE" sticker doesn't change the box's contents, but it changes how movers must handle it. Similarly, the Send marker trait adds no methods to your struct, but it tells the compiler it's safe to move that struct to another thread.
HOW IT WORKS A marker trait is an empty trait definition, like trait MyMarker {}. A type implements it to signal it has a certain property. The compiler then uses these trait bounds to constrain generic functions. For example, a function that spawns a thread might require its arguments to be Send. Many core marker traits, like Send and Sync, are automatically implemented by the compiler for structs and enums if all their fields also implement them.
WHEN TO USE IT You use marker traits constantly when writing generic or concurrent Rust by specifying bounds like T: Copy or T: Send. You can also define your own to enforce invariants on custom generic data structures. For example, a custom concurrent queue would require the items it holds to be Send.
WHEN NOT TO USE IT Don't create a marker trait when you need to define behavior. If a trait should enable new functionality by providing methods to call, it's a regular trait, not a marker. The purpose of a marker is purely for classification and compile-time checks, not for polymorphism via dynamic dispatch.
ONE CANONICAL EXAMPLE The Send trait indicates a type can be safely transferred to another thread. A raw pointer like *mut T is not Send because it offers no thread-safety guarantees. If you create a struct MyType { data: *mut u8 }, the compiler will not automatically derive Send for MyType. This prevents you from accidentally causing data races. To make it Send, you must either wrap the pointer in a safe abstraction or use an unsafe impl Send for MyType, explicitly telling the compiler you are upholding the necessary invariants yourself.
Read the original → doc.rust-lang.org
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.