Rust traits

From wikinotes
Revision as of 20:09, 8 February 2023 by Will (talk | contribs)

Traits are similar to interfaces except they can have a default implementation.

Basics

Define and implement trait

struct Cat { name: String }
struct Dog { name: String, breed: String }

// all implementors of 'Pet' must have method 'play' with this method signature
trait Pet {
    fn play(&self);
}


impl Pet for Cat {
    fn play(&self) {
        println!("you give a ball of yarn to {}", self.name)
    }
}

Use trait implementors in method signature

// use trait as param type
fn play_with_pet(pet: &impl Pet) {
    pet.play();
}

let cat = Cat{name: "maize".to_string()};
let dog = Dog{name: "midnight".to_string(), breed: "?".to_string()};
play_with_pet(&cat);
play_with_pet(&dog);

Trait Bound Syntax (Generic)

You can express the same trait implementation as a generic (allowing you to bind the trait to multiple variables).

struct Cat { name: String }
struct Dog { name: String, breed: String }

trait Pet {
    fn play(&self);
}

fn play<P: Pet>(&p) {
    println!(p.play());
}

Object must implement multiple traits

Using trait-bound syntax

// `p` must implement both Pet and Display
fn play(p: &(impl Pet + Display)) {
    println!(p.play());
}

Using where syntax

fn play<P>(p: &P)
where
    P: Pet + Display
{
    // ...
}

Default Method Implementation

Unlike interfaces in most other languages, you can define a default method implementation on the trait.

TODO:

can you require fields as well? (in this case, name)

struct Cat { name: String }
struct Dog { name: String, breed: String }

trait Pet {
    fn play(&self) {
        println!("rub")
    }
}