Traits in Rust
In this lesson, you will learn about traits in Rust. Traits define shared behavior that multiple types can implement. They are similar to interfaces in other programming languages, but more powerful and flexible.
Traits are a core part of Rust’s type system and are heavily used in generic programming.
What Is a Trait?
A trait defines a set of methods that a type must implement. It describes what a type can do, not what it is.
Traits allow Rust to achieve polymorphism without inheritance.
Defining a Trait
You define a trait using the trait keyword.
trait Speak {
fn speak(&self);
}
This trait defines a behavior called speak that any implementing type must provide.
Implementing a Trait
Types implement traits using the impl keyword.
struct Dog;
struct Cat;
impl Speak for Dog {
fn speak(&self) {
println!("Dog says: Woof!");
}
}
impl Speak for Cat {
fn speak(&self) {
println!("Cat says: Meow!");
}
}
Both Dog and Cat implement the same trait but behave differently.
Calling Trait Methods
Once a trait is implemented, you can call its methods like regular functions.
let dog = Dog;
let cat = Cat;
dog.speak();
cat.speak();
Traits as Function Parameters
Traits can be used to accept different types that share the same behavior.
fn make_speak(animal: &impl Speak) {
animal.speak();
}
This function works with any type that implements Speak.
Trait Bounds
Trait bounds provide another way to specify trait requirements using generics.
fn make_speak(animal: &T) {
animal.speak();
}
This approach is commonly used in generic functions.
Default Trait Methods
Traits can provide default implementations for methods.
trait Describe {
fn describe(&self) {
println!("This is an object.");
}
}
Types can override the default behavior if needed.
Implementing Multiple Traits
A type can implement multiple traits.
trait Walk {
fn walk(&self);
}
impl Walk for Dog {
fn walk(&self) {
println!("Dog is walking");
}
}
This makes Rust highly flexible without using inheritance.
Traits in the Standard Library
Rust’s standard library heavily relies on traits such as:
DebugCloneCopyDisplay
#[derive(Debug)]
struct User {
name: String,
}
Why Traits Are Important
Traits enable:
- Code reuse without inheritance
- Clean abstractions
- Powerful generic programming
- Safe polymorphism
📝 Practice Exercises
Exercise 1
Create a trait called Draw with a method draw().
Exercise 2
Implement the Draw trait for a struct Circle.
Exercise 3
Write a function that accepts any type implementing Draw.
Exercise 4
Add a default method to a trait and override it.
✅ Practice Answers
Answer 1
trait Draw {
fn draw(&self);
}
Answer 2
struct Circle;
impl Draw for Circle {
fn draw(&self) {
println!("Drawing a circle");
}
}
Answer 3
fn render(item: &impl Draw) {
item.draw();
}
Answer 4
trait Info {
fn info(&self) {
println!("Default info");
}
}
struct BoxItem;
impl Info for BoxItem {}
What’s Next?
In the next lesson, you will learn about Generics — how Rust writes flexible, reusable code using type parameters.