Introduction
Sometimes you need ‘mark’ certain types with behaviours or properties. In Rust you can do this by using a marker trait, or in Rust lingo: tag traits. You may know some of these:
Copy
You can use this trait to mark any type that can do a bit-wise copy
Send
You can use this to identify types which you safely send over thread boundaries
Sync
If you want to tag a type which can be safely shared between threads, use this tag trait
Sized
This trait you can use to identify types whose size is know at compile time.
Unpin
You can move types with this tag after you pinned them.
Many of these trait, like Send
and Sync
are automatically implemented by the compiler, when it is safe to do so.
We can also implement our own marker traits, and that is what I will do in this post
Implementing a marker trait in Rust
Imagine we have a system whether we want to tag objects as printable or not. We could simply do it like this:
struct PrintableType;
impl Printable for PrintableType {}
fn print_object<T: Printable>(_object: &T) {
println!("Printable");
}
This simply says: the print_object()
method only takes objects tagged with the Printable
interface. If you happen to pass an object to the method without a tag, the compiler will raise an error. The underscore before the _object
parameter is to identify that it is not being used.
Testing time
Now we can run a very simple test:
fn main() {
let printable = PrintableType;
print_object(&printable);
}
This code is so simple, it explains itself.
Conclusion
This is one of the simpler patterns, but as you can see it is widely used in the standard library: most of the traits come from the std::markers
module in the standard library. But as you can see, it is quite easy to build your own using Rust’s advanced typing system. I think I must say this is probably a pattern you are not going to need very much.