A marker trait indicates an attribute of a Rust type without necessarily adding behavior. It is often empty: its purpose is to let other code require a property at compile time. This is a specialized technique, useful when a named constraint makes code clearer than repeating trait bounds. section 7.5 section 7.0
Listing 7.2 Full-featured marker trait
#[derive(
Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd,
)]
struct KitchenSink; #1
trait FullFeatured {} #2
impl<T> FullFeatured for T where #3
T: Clone
+ Copy
+ std::fmt::Debug
+ Default
+ Eq
+ std::hash::Hash
+ Ord
+ PartialEq
+ PartialOrd
{
}
`FullFeatured` has no methods. Its blanket implementation marks every type that satisfies the listed bounds; the derived `KitchenSink` type satisfies them without an explicit `impl FullFeatured`. section 7.5
Require the marker in a generic type
`struct Container<T: FullFeatured> { t: T }` requires its element type to meet the marker’s bounds. With the blanket implementation above, `Container { t: KitchenSink {} }` is valid. The marker gives the constraint a name instead of repeating its list of traits at each use. section 7.5
Keep the distinction between marking and behavior clear. `Send` and `Sync` are marker traits without methods, but implementing either manually requires `unsafe`; only the compiler can do so safely. A supertrait declaration such as `trait CloneAndDebug: Clone + Debug {}` expresses required traits directly. Although a trait with methods can also be treated as a marker, an empty trait is generally less confusing. `PhantomData` is different: it is a marker struct used to represent a generic parameter without storing a value of that type. section 7.5 section 2.1.5

