Rust Traits: Defining Shared Behavior
Rust traits are like contracts that guarantee a type has certain methods, similar to interfaces. This lets you write functions that operate on any type with that behavior, like a `summarize` method for both articles and posts.
WHY IT EXISTS How do you write a single function that can operate on different types of data, like a news article and a social media post? Without a common blueprint, you'd need separate functions. Traits solve this by defining a shared set of behaviors that different types can promise to uphold, enabling code reuse and abstraction.
THE MENTAL MODEL A trait is a contract or an interface. It's a list of methods that a type must implement to be considered part of a group. If a NewsArticle struct and a SocialPost struct both implement the Summarizable trait, you know for a fact that you can call .summarize() on either one, even though the underlying data and logic are completely different.
HOW IT WORKS You define a trait using the trait keyword, followed by the method signatures it requires. For example: pub trait Summary { fn summarize(&self) -> String; }. Notice there's no method body, just a signature ending in a semicolon. Then, for each type that needs this behavior, you provide a concrete implementation using an impl block: impl Summary for NewsArticle { ... }. The compiler enforces this contract, ensuring any type that claims to have the Summary trait actually provides a valid summarize method.
WHEN TO USE IT Use traits to define behavior that can be shared across disparate types. This is the foundation of polymorphism in Rust. It's essential for writing generic functions that can accept any type that fulfills a certain contract (e.g., fn notify<T: Summary>(item: &T)). It allows libraries to define extension points for users to implement on their own types.
WHEN NOT TO USE IT Don't use a trait for methods that are specific to a single, concrete type; a standard impl MyStruct block is for that. Traits are for abstract, shared behavior. Also, be aware of the 'orphan rule': you can't implement an external trait for an external type in your crate, as it would be unclear which implementation to use if multiple crates did this.
ONE CANONICAL EXAMPLE Define a Summary trait with a summarize method. Create two structs, NewsArticle and SocialPost. Implement Summary for both. The NewsArticle implementation might return headline, by author. The SocialPost implementation might return username: content. Now you can create a function print_summary(item: &impl Summary) that accepts an instance of either struct and successfully calls .summarize() on it, abstracting away the concrete type.
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.