Trait in Rust programming
I believe people those who are from object oriented programming languages understand the usage of Trait easily . I have seen people saying Trait are similar to Interfaces in Java.But we will start with zero perquisites or background knowledge on those. Only prerequisite would be to basic understanding on how “Struct” or user defined data types are created in Rust.
Trait - Collection of methods defined for unknown types.
The definition for Trait is an abstract over behavior that types can have in common.don’t worry , if we are not able to digest the definition fully.
You might have noticed that in the real world lot of objects shares same behavior and they give the output to the behavior differently.
eg: Car and Bus can provide “ride”
Cat and Dog can have “talk” method
in a Game different objects can have “bonus”
We know these methods/functions are needed for these objects ,but they should produce different output for them differently for different types of objects.
eg : when Cat call “talk” method output would be “Meow Meow”
when Dog call talk method output would be “woof woof”
Also we don’t want other types of objects calling the method if they are not implemented
eg: in my case Cow can’t talk
It would be great if I get noticed in the compile time itself that there is no implementation for Cow for “talk” . rather than future at some point of time in the future the code goes through that piece code and fails badly.
So we can identify the shared behavior of objects in the design phase of your project, the ideal way of defining these common behavior will be through “Trait”
Now you have understood the use case of Trait .
I think we can jump into an example
example creates 3 structs for Cat, Dog and Cow
/*We create/define a trait ( unknown type) using keyword "trait" called Speaker and define the behavior of the trait.
in this example trait accept the unknown type which implements the trait and returns nothing.
Trait name is Speaker and method defined is speak. So we are going to call this "speak" method for the struct object implements this.
*/
pub trait Speaker {
fn speak(&self) -> ();
}
/*Next step would be implementing the trait for whichever types have the common behavior.
This case our Cat and Dog speaks.
so implemented them.*/
impl Speaker for Cat {
fn speak(&self) { println!("meow!"); }
}
/* Now we can call them from "main" function as it is in scope.First we need to create struct object and then call the trait defined method (speak) with dot(.) operator. */
cat1.speak();
pub struct Cat{
color:(u8,u8,u8),
}
pub struct Dog{
color:(u8,u8,u8),
}
pub struct Cow{
color:(u8,u8,u8),
}
pub trait Speaker {
fn speak(&self) -> ();
}
impl Speaker for Cat {
fn speak(&self) {
println!("meow!");
}
}
impl Speaker for Dog {
fn speak(&self) {
println!("woof!");
}
}
fn main(){
let cat1 = Cat{color:(30,30,30)};
let dog1 = Dog{color:(30,30,30)};
let cow1 = Cow{color:(30,30,30)};
cat1.speak();
dog1.speak();
//cow1.speak(); fails in compile time saying "no implementaton of the trait"
}
Trait BoundsTrait bounds restricts the function to have the types which implements the trait.So the function can not be called with any other types.Code that calls the function with any other type, like a String or an i32, won’t compile, because those types don’t implement "Speaker"
pub struct Cat{
color:(u8,u8,u8),
}
pub struct Dog{
color:(u8,u8,u8),
}
pub struct Cow{
color:(u8,u8,u8),
}
pub trait Speaker {
fn speak(&self) -> ();
}
impl Speaker for Cat {
fn speak(&self) {
println!("meow!");
}
}
impl Speaker for Dog {
fn speak(&self) {
println!("woof!");
}
}
pub fn notify(speaker: S) {
speaker.speak();
}
fn main(){
let cat1 = Cat{color:(30,30,30)};
let dog1 = Dog{color:(30,30,30)};
let cow1 = Cow{color:(30,30,30)};
notify(cat1);
// notify(cow1); uncomment this line to see the compile time error.
}
In Rust a trait must be in scope for you to be able to call its methods.Reference
https://doc.rust-lang.org/book/ch10-02-traits.html