From 4c18f833b07ffcc1ea89e5774e55b706d5c7dee3 Mon Sep 17 00:00:00 2001 From: darkicewolf50 Date: Thu, 30 Jan 2025 21:42:15 +0000 Subject: [PATCH] halfway through ch10.2 --- Traits.md | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) diff --git a/Traits.md b/Traits.md index 0278085..150c390 100644 --- a/Traits.md +++ b/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` 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` 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