mirror of
https://github.com/darkicewolf50/RustBrock.git
synced 2025-06-15 13:04:18 -06:00
halfway through ch10.2
This commit is contained in:
parent
1b87644934
commit
4c18f833b0
181
Traits.md
181
Traits.md
@ -11,3 +11,184 @@ We can use *trait bounds* to specifty that a generic type can be any type that h
|
||||
Note: Traits are similar to a feature often called *interfaces* in other languages, but there are some differences in Rust
|
||||
|
||||
## Defining a Trait
|
||||
|
||||
A type's behavior consists of the methods we can call on that type
|
||||
|
||||
Some types can share the same behavior if we can call the same methods on all of those types.
|
||||
|
||||
Trait definitions are a way to gropu method singatures together to define a set of behaviors necessary to accomplish something.
|
||||
|
||||
Here is an exmaple to lay this out
|
||||
|
||||
lets say you have multiple structs that hold various kinds and amounts of text
|
||||
`NewsArticle` struct that holds a news story filed in a particular locaion
|
||||
`Tweet` that can have, at most, 280 characters along with metadata that indicates whether it was a new tweet, a retweet, or a reply to another tweet
|
||||
|
||||
We want to make a meida aggregator library crate named `aggregator` that can display summaries of data that could be stored in a `NewsArticle` or `Tweet`
|
||||
|
||||
In order to do this we need a summary from each type, this would be done by calling a `summarize` method on an instance
|
||||
|
||||
Example definition of a trait for the above situation
|
||||
```rust
|
||||
pub trait Summary {
|
||||
fn summarize(&self) -> String;
|
||||
}
|
||||
```
|
||||
|
||||
Inside the trait named scope is where method signatures are defined
|
||||
|
||||
After the method signature, instead of providing an implmentation within the curly brackets, you use a semicolon
|
||||
|
||||
Each type implementing this trait must provide its own custom behavor for the body of the method
|
||||
|
||||
The compliler will enfore that any type that has the `Summary` trait will havee the method `summarize` defined with this signature
|
||||
|
||||
Note: a trait can habe multiple methods in its body, each method signature are listed one per line and each lne ends in a semicolon
|
||||
|
||||
## Implementing a Trait on a Type
|
||||
|
||||
Now that the desirred signatures of the `Summary` trait's methods, we can implement it on the types in our media aggregator
|
||||
|
||||
Here is an example where the trait is implmented for both `NewsArticle` and `Tweet` structs
|
||||
```rust
|
||||
pub struct NewsArticle {
|
||||
pub headline: String,
|
||||
pub location: String,
|
||||
pub author: String,
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
impl Summary for NewsArticle {
|
||||
fn summarize(&self) -> String {
|
||||
format!("{}, by {} ({})", self.headline, self.author, self.location)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Tweet {
|
||||
pub username: String,
|
||||
pub content: String,
|
||||
pub reply: bool,
|
||||
pub retweet: bool,
|
||||
}
|
||||
|
||||
impl Summary for Tweet {
|
||||
fn summarize(&self) -> String {
|
||||
format!("{}: {}", self.username, self.content)
|
||||
}
|
||||
}
|
||||
```
|
||||
Implementing this is similar to implementing regular methods.
|
||||
|
||||
The difference is after the `impl` we put the trait name we want to implement, then use the `for` keyword, then put the specify the name of the type we want to implement the trait for.
|
||||
|
||||
In the `impl` block we put the method's singaure that the trait has defined, but instead of a semicolon you put the implementation with its behavior in curly braces.
|
||||
|
||||
Now that the library has implemented thr `Summary` trait on `NewsArticle` and `Tweet`, users of the crate can call the trait methods on instances of `NewsArticle` and `Tweet` in the same way regular methods, the only difference is that you need to bring the trait into scope as well as the type
|
||||
|
||||
Here is an example of how a binary crate could use our `aggregator` library crate
|
||||
```rust
|
||||
use aggregator::{Summary, Tweet};
|
||||
|
||||
fn main() {
|
||||
let tweet = Tweet {
|
||||
username: String::from("horse_ebooks"),
|
||||
content: String::from(
|
||||
"of course, as you probably already know, people",
|
||||
),
|
||||
reply: false,
|
||||
retweet: false,
|
||||
};
|
||||
|
||||
println!("1 new tweet: {}", tweet.summarize());
|
||||
}
|
||||
```
|
||||
|
||||
Other cates that depend on the `aggregator` crate can also bring the `Summary` trait into scope to implement `Summary` on their own type
|
||||
|
||||
One restriction to note is that we can implement a trait on a type only if either the trait or the tpye, or both are local to your crate
|
||||
|
||||
For example, you can implement standard library traits like `Display` on custom tpye like `Tweet` as part of our `aggregator` crate functionality because the type `Tweet` is local to our `aggregator` crate
|
||||
|
||||
We could also implement `Summary` on `Vec<T>` in our `aggregator` crate because the trait `Summary` is local to our `aggregator` crate
|
||||
|
||||
You cant implement external trait on external types
|
||||
|
||||
For example you cant implement `Display` trait on `Vec<T>` within our aggregator create they are both defined in the std library which are both not local to our `aggregator` crate
|
||||
|
||||
This restriction is part of a property called *coherence*, and more specifically the *orphan rule*, this is named becuase the parent type is not present
|
||||
|
||||
This rule ensures that other people's code can't breake your code and vice versa.
|
||||
|
||||
Without this rule the compiler could or would get confused about two implmentations of the same trait on the same type in tow different crates
|
||||
|
||||
## Default Implementations
|
||||
This is sometimes useful to have defualt behavior for some or all of the methods in a trait instead of requiring implementaitons for all method on every type.
|
||||
|
||||
This could be overridden by implementation on the type itself of the trait with the speicific singature
|
||||
|
||||
Here is an example of this
|
||||
```rust
|
||||
pub trait Summary {
|
||||
fn summarize(&self) -> String {
|
||||
String::from("(Read more...)")
|
||||
}
|
||||
}
|
||||
```
|
||||
To use the defualt implementation to summarize instances of `NewArticel`, we specifiy an empty `impl` block with `impl Summary for NewsArticle {}`
|
||||
|
||||
Even though `summarize` is not defined on `NewsArticle` directly we have a defualt implementation and we specified that `NewsArticle` implements the `Summary` trait
|
||||
|
||||
The provided defualt implementation allows you to do something like this, even though the implemntation function scope is empty
|
||||
```rust
|
||||
let article = NewsArticle {
|
||||
headline: String::from("Penguins win the Stanley Cup Championship!"),
|
||||
location: String::from("Pittsburgh, PA, USA"),
|
||||
author: String::from("Iceburgh"),
|
||||
content: String::from(
|
||||
"The Pittsburgh Penguins once again are the best \
|
||||
hockey team in the NHL.",
|
||||
),
|
||||
};
|
||||
|
||||
println!("New article available! {}", article.summarize());
|
||||
```
|
||||
|
||||
This still prints `New article available! (Read more...)`
|
||||
|
||||
Default implmentations can call other methods in the smae trait, even if other methods don't have a default implementations.
|
||||
|
||||
For example
|
||||
```rust
|
||||
pub trait Summary {
|
||||
fn summarize_author(&self) -> String;
|
||||
|
||||
fn summarize(&self) -> String {
|
||||
format!("(Read more from {}...)", self.summarize_author())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This default implementation only requires the signuare in order to use, it relies that the implementation in the type, which the compiler ensures
|
||||
|
||||
To use thus version of `Summary`, we only need to define `summarize_author` when we implement the trait on a type
|
||||
|
||||
Because we have implemented `summarize_author`, the `Summary` trait has given the behavior of the `summarize` method without requiring us to write any more code
|
||||
|
||||
Here is the use of the `summarize_author` trait method
|
||||
```rust
|
||||
let tweet = Tweet {
|
||||
username: String::from("horse_ebooks"),
|
||||
content: String::from(
|
||||
"of course, as you probably already know, people",
|
||||
),
|
||||
reply: false,
|
||||
retweet: false,
|
||||
};
|
||||
|
||||
println!("1 new tweet: {}", tweet.summarize());
|
||||
```
|
||||
This would print `1 new tweet: (Read more from @horse_ebooks...)`
|
||||
|
||||
Note: it is impossible to call the default implementation from an overridden implementation of that same method
|
||||
|
||||
## Traits as Parameters
|
||||
|
Loading…
x
Reference in New Issue
Block a user