Rust traits: Difference between revisions

From wikinotes
No edit summary
No edit summary
Line 10: Line 10:
// all implementors of 'Pet' must have method 'play' with this method signature
// all implementors of 'Pet' must have method 'play' with this method signature
trait Pet {
trait Pet {
     fn play(&self);
     fn play(&self) -> bool;
}
}




impl Pet for Cat {
impl Pet for Cat {
     fn play(&self) {
     fn play(&self) -> bool {
         println!("you give a ball of yarn to {}", self.name)
         println!("you give a ball of yarn to {}", self.name);
        true
     }
     }
}
}
Line 24: Line 25:
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
// use trait as param type
// use trait as param type
fn play_with_pet(pet: &impl Pet) {
fn play_with_pet(pet: &impl Pet) -> bool {
     pet.play();
     pet.play();
}
}
Line 35: Line 36:
</blockquote><!-- Basics -->
</blockquote><!-- Basics -->


= Trait Bound Syntax (Generic) =
= In Function Signatures =
<blockquote>
<blockquote>
You can express the same trait implementation as a generic (allowing you to bind the trait to multiple variables).
== As Param ==
<blockquote>
<syntaxhighlight lang="rust">
// with impl
fn play_with_pet(pet: &impl Pet) -> bool {}
 
// trait-bound-syntax (generic)
fn play_with_pet<P: Pet>(pet: P) -> bool {}
</syntaxhighlight>


Requires object implements multiple interfaces
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
struct Cat { name: String }
// with impl
struct Dog { name: String, breed: String }
fn play_with_pet(pet: &(impl Pet + Display)) -> bool {}


trait Pet {
// with where clause
    fn play(&self);
fn play_with_pet<P>(p: P) -> bool
}
where
 
     P: Pet + Display
fn play<P: Pet>(&p) {
{}
     println!(p.play());
}
</syntaxhighlight>
</syntaxhighlight>
</blockquote><!-- As Generic -->
</blockquote><!-- Params -->


= Object must implement multiple traits =
== As Return ==
<blockquote>
<blockquote>
Using trait-bound syntax
If you're only ever returning one possible concretion, you can do this:
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
// `p` must implement both Pet and Display
fn gimme_pet() -> impl Pet {
fn play(p: &(impl Pet + Display)) {
     // ...
     println!(p.play());
}
}
</syntaxhighlight>
</syntaxhighlight>


Using <code>where</code> syntax
But if you may return different types of implementors of Pet, you'll need to do this:
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
fn play<P>(p: &P)
// TODO
where
    P: Pet + Display
{
    // ...
}
</syntaxhighlight>
</syntaxhighlight>
</blockquote><!-- Object must implement multiple traits -->
</blockquote><!-- As Return -->
</blockquote><!-- Method Signatures -->


= Default Method Implementation =
= Default Method Implementation =

Revision as of 20:23, 8 February 2023

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) -> 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:

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