Rust Traits

Shared behavior and interfaces for types

🔗 What are Traits?

Traits define shared behavior that types can implement. They're like interfaces in other languages, allowing different types to share common functionality and enabling powerful abstractions.


trait Drawable {
    fn draw(&self);
}
                                    
Trait Features

Trait Features

📋

Method Signatures

Define required methods

trait Summary {
    fn summarize(&self) -> String;
}
⚙️

Default Implementations

Provide default behavior

trait Greet {
    fn greet(&self) {
        println!("Hello!");
    }
}
🔄

Trait Bounds

Constrain generic types

fn process(item: T) {}
📦

Trait Objects

Dynamic dispatch

Box

🔹 Defining and Implementing Traits

Create traits and implement them for different types:

// Define a trait
trait Animal {
    fn name(&self) -> &str
    fn sound(&self) -> &str
    
    // Default implementation
    fn speak(&self) {
        println!("{} says {}", self.name(), self.sound());
    }
}

// Implement for different types
struct Dog {
    name: String,
}

struct Cat {
    name: String,
}

impl Animal for Dog {
    fn name(&self) -> &str {
        &self.name
    }
    
    fn sound(&self) -> &str {
        "Woof!"
    }
}

impl Animal for Cat {
    fn name(&self) -> &str {
        &self.name
    }
    
    fn sound(&self) -> &str {
        "Meow!"
    }
    
    // Override default implementation
    fn speak(&self) {
        println!("{} purrs and says {}", self.name(), self.sound());
    }
}

fn main() {
    let dog = Dog { name: String::from("Buddy") };
    let cat = Cat { name: String::from("Whiskers") };
    
    dog.speak();
    cat.speak();
}

Output:

Buddy says Woof!
Whiskers purrs and says Meow!

🔹 Trait Bounds

Use traits to constrain generic functions:

use std::fmt::Display;

trait Printable {
    fn print(&self);
}

impl Printable for T {
    fn print(&self) {
        println!("Value: {}", self);
    }
}

// Function with trait bound
fn print_twice(item: T) {
    println!("First: {}", item);
    println!("Second: {}", item.clone());
}

// Multiple trait bounds
fn compare_and_print(a: T, b: T) 
where 
    T: Display + PartialOrd + Clone,
{
    if a > b {
        println!("{} is greater than {}", a, b);
    } else if a < b {
        println!("{} is less than {}", a, b);
    } else {
        println!("{} equals {}", a, b);
    }
}

fn main() {
    print_twice(42);
    print_twice("Hello".to_string());
    
    compare_and_print(10, 5);
    compare_and_print("apple", "banana");
}

Output:

First: 42
Second: 42
First: Hello
Second: Hello
10 is greater than 5
apple is less than banana

🔹 Trait Objects

Use traits for dynamic dispatch:

trait Shape {
    fn area(&self) -> f64;
    fn perimeter(&self) -> f64;
}

struct Circle {
    radius: f64,
}

struct Rectangle {
    width: f64,
    height: f64,
}

impl Shape for Circle {
    fn area(&self) -> f64 {
        3.14159 * self.radius * self.radius
    }
    
    fn perimeter(&self) -> f64 {
        2.0 * 3.14159 * self.radius
    }
}

impl Shape for Rectangle {
    fn area(&self) -> f64 {
        self.width * self.height
    }
    
    fn perimeter(&self) -> f64 {
        2.0 * (self.width + self.height)
    }
}

fn print_shape_info(shape: &dyn Shape) {
    println!("Area: {:.2}", shape.area());
    println!("Perimeter: {:.2}", shape.perimeter());
}

fn main() {
    let shapes: Vec> = vec![
        Box::new(Circle { radius: 5.0 }),
        Box::new(Rectangle { width: 4.0, height: 6.0 }),
    ];
    
    for (i, shape) in shapes.iter().enumerate() {
        println!("Shape {}:", i + 1);
        print_shape_info(shape.as_ref());
        println!();
    }
}

Output:

Shape 1:
Area: 78.54
Perimeter: 31.42

Shape 2:
Area: 24.00
Perimeter: 20.00

🔹 Deriving Common Traits

Automatically implement common traits:

#[derive(Debug, Clone, PartialEq, PartialOrd)]
struct Person {
    name: String,
    age: u32,
}

#[derive(Debug, Copy, Clone, PartialEq)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let person1 = Person {
        name: String::from("Alice"),
        age: 30,
    };
    
    let person2 = person1.clone(); // Clone trait
    
    println!("{:?}", person1); // Debug trait
    println!("Are they equal? {}", person1 == person2); // PartialEq trait
    
    let point1 = Point { x: 1, y: 2 };
    let point2 = point1; // Copy trait (no move)
    
    println!("Point 1: {:?}", point1);
    println!("Point 2: {:?}", point2);
    
    // Comparing people
    let person3 = Person {
        name: String::from("Bob"),
        age: 25,
    };
    
    if person1 > person3 {
        println!("{} is older than {}", person1.name, person3.name);
    }
}

Output:

Person { name: "Alice", age: 30 }
Are they equal? true
Point 1: Point { x: 1, y: 2 }
Point 2: Point { x: 1, y: 2 }
Alice is older than Bob

🧠 Test Your Knowledge

What keyword is used to define a trait in Rust?