Rust traits

From wikinotes
Revision as of 04:22, 11 February 2023 by Will (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Traits are similar to interfaces with some key differences:

  • they can have a default implementation.
  • they can behave like refinements, only adding the method if the trait has been imported into namespace

Basics

NOTE:

If the trait method implementation is in a different file than your type,
you'll need to import the trait into the current namespace so it is usable on your object!
ex. std::io::BufRead

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) -> bool;
}


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

Use trait implementors in method signature

// use trait as param type
fn play_with_pet(pet: &impl Pet) -> bool {
    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);

In Function Signatures

As Param

// with impl
fn play_with_pet(pet: &impl Pet) -> bool {}

// trait-bound-syntax (generic)
fn play_with_pet<P: Pet>(pet: P) -> bool {}

Requires object implements multiple interfaces

// with impl
fn play_with_pet(pet: &(impl Pet + Display)) -> bool {}

// with where clause
fn play_with_pet<P>(p: P) -> bool
where
    P: Pet + Display
{}

As Return

If you're only ever returning one possible concretion, you can do this:

fn gimme_pet() -> impl Pet {
    // ...
}

But if you may return different types of implementors of Pet, you'll need to do this: https://doc.rust-lang.org/stable/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types

// TODO

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")
    }
}

Blanket Implementations

You can write traits that are automatically applied all objects,
that implements another trait.

// every object that implements `Display`
// will automagically be assigned this `to_string()` method.

impl<T: Display> ToString for T {
    fn to_string(v: T) -> String {
        format!("{}", )
    }
}

Builtin Traits

There are several builtin traits.
Some must be imported into scope for methods to be available (like refinements),
Others are applied automatically to objects.
These builtin traits are documented (non-exhaustively) in other sections.

rust pointers
rust threading