As the title says, traits
are Rust’s alternative to Interfaces. They allow us to use polymorphism in Rust. We can create a trait like this:
1
2
3
trait Calculator {
fn add(&self, left: i32, right: i32) -> i32;
}
To implement the trait we use the impl
keyword on a struct:
1
2
3
4
5
6
7
struct GoodCalculator {}
impl Calculator for GoodCalculator {
fn add(&self, left: i32, right: i32) -> i32 {
left + right
}
}
Traits (like interfaces) are useful as function parameters, so different types can be passed. The simplest way to receive a trait as a parameter is using the impl
keyword like this:
1
2
3
fn add_using_calculator(calculator: &impl Calculator) {
println!("The result of adding {} and {} is: {}", 10, 5, calculator.add(10, 5));
}
When we need a type to implement multiple interfaces, we can use this syntax:
1
2
3
4
fn add_and_print(the_thing: &(impl Calculator + Printer)) {
the_thing.print();
println!("The result of adding {} and {} is: {}", 10, 5, the_thing.add(10, 5));
}
Another syntax:
1
2
3
4
fn add_and_print<T: Calculator + Printer>(the_thing: &T) {
the_thing.print();
println!("The result of adding {} and {} is: {}", 10, 5, the_thing.add(10, 5));
}
When we have multiple arguments using the following syntax is preferred:
1
2
3
4
5
6
7
fn add_and_print<T>(the_thing: &T)
where
T: Calculator + Printer
{
the_thing.print();
println!("The result of adding {} and {} is: {}", 10, 5, the_thing.add(10, 5));
}
Abstract methods / Default implementations
Sometimes we want to provide default implementations for some methods on our traits. This can easily be done:
1
2
3
4
5
trait SoundMaker {
fn print(&self) {
println!("Default implementation")
}
}
Conclusion
Traits in Rust, work similarly to interfaces in Golang. I added some code using the above examples to Github so you can see it in action.
programming
rust
]