Loading...
Loading...

Rust Traits and Generics

Traits define shared behavior that types can implement, while generics enable writing flexible, reusable code that works with multiple types. Together they form Rust's approach to polymorphism while maintaining compile-time type safety and zero-cost abstractions.

1. Defining and Implementing Traits

// Define a trait
trait Greet {
    fn say_hello(&self);  // Method signature
    
    // Default implementation
    fn say_goodbye(&self) {
        println!("Goodbye!");
    }
}

// Implement for a type
struct Person;

impl Greet for Person {
    fn say_hello(&self) {
        println!("Hello from Person!");
    }
}

// Using the trait
let person = Person;
person.say_hello();  // Uses custom implementation
person.say_goodbye(); // Uses default implementation

2. Using Traits with Generics

// Function with trait bound
fn notify<T: Greet>(item: T) {
    item.say_hello();
}

// Alternative syntax
fn notify_where<T>(item: T)
where
    T: Greet,
{
    item.say_hello();
}

// Multiple trait bounds
fn clone_and_notify<T>(item: T) -> T
where
    T: Greet + Clone,
{
    item.say_hello();
    item.clone()
}

3. Important Standard Library Traits

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

// Using From/Into for conversions
impl From<(i32, i32)> for Point {
    fn from((x, y): (i32, i32)) -> Self {
        Point { x, y }
    }
}

let point = Point::from((1, 2));
let point2: Point = (3, 4).into();

// Iterator trait
impl Iterator for Counter {
    type Item = u32;
    
    fn next(&mut self) -> Option<Self::Item> {
        // Implementation...
    }
}

4. Associated Types in Traits

trait Container {
    type Item;  // Associated type
    
    fn add(&mut self, item: Self::Item);
    fn get(&self, index: usize) -> Option<&Self::Item>;
}

impl Container for Vec<i32> {
    type Item = i32;
    
    fn add(&mut self, item: i32) {
        self.push(item);
    }
    
    fn get(&self, index: usize) -> Option<&i32> {
        self.get(index)
    }
}

5. Blanket Implementations

// Implement trait for all types that meet bounds
impl<T: Display> Greet for T {
    fn say_hello(&self) {
        println!("Hello from {}!", self);
    }
}

// Now any Display type has Greet methods
let num = 42;
num.say_hello(); // "Hello from 42!"

6. Generic Data Structures

struct Pair<T> {
    first: T,
    second: T,
}

impl<T> Pair<T> {
    fn new(first: T, second: T) -> Self {
        Self { first, second }
    }
}

// Specialized implementation
impl Pair<f32> {
    fn distance(&self) -> f32 {
        (self.first.powi(2) + self.second.powi(2)).sqrt()
    }
}

let pair = Pair::new(5, 10);
let float_pair = Pair::new(3.0, 4.0);
println!("Distance: {}", float_pair.distance()); // 5.0

7. Dynamic Dispatch with Trait Objects

trait Draw {
    fn draw(&self);
}

struct Circle;
struct Square;

impl Draw for Circle {
    fn draw(&self) { println!("Drawing circle"); }
}

impl Draw for Square {
    fn draw(&self) { println!("Drawing square"); }
}

// Dynamic dispatch at runtime
let shapes: Vec<Box<dyn Draw>> = vec![
    Box::new(Circle),
    Box::new(Square),
];

for shape in shapes {
    shape.draw();
}
0 Interaction
0 Views
Views
0 Likes
×
×
×
🍪 CookieConsent@Ptutorials:~

Welcome to Ptutorials

$ Allow cookies on this site ? (y/n)

top-home