Traits

Using traits in Rust helps to encapsulate shared behavior, making the code more reusable, modular and easier to understand. These are similar to interfaces in other programming languages that allow you to define a set of methods that a type must implement, promoting polymorphism.

trait Describable {
    fn describe(&self) -> String;
}

struct Dog {
    name: String,
    age: u8,
}

// implement the trait for the struct 
impl Describable for Dog {
    fn describe(&self) -> String {
        format!("Dog named {} is {} years old.",
self.name, self.age)
    }
}

// Implement the trait for another struct
struct Car {
    model: String,
    year: u16,
}

impl Describable for Car {
    fn describe(&self) -> String {
        format!("Car model {} from year {}.",
self.model, self.year)
    }
}

fn main() {
    let my_dog = Dog { name:String::from("Buddy"), age: 3 };
    let my_car = Car { model: String::from("Tesla"), year: 2020 };
    // Use the shared behavior
    println!("{}", my_dog.describe());
    println!("{}", my_car.describe());
}

Type conversion

The From and Into traits in Rust provide a standardized way to convert between types.

struct MyType {
    value: i32,
}
impl From<i32> for MyType {
    fn from(item: i32) -> Self {
        MyType { value: item }
    }
}
fn main() {
    let num = 42;

    let my_value: MyType = MyType::from(num);
    println!("MyType value: {}", my_value.value);

    let original_value: i32 = my_value.into();
    println!("Original value: {}", original_value);
}