|
| 1 | +use std::fmt::{Debug, Display}; |
| 2 | + |
| 3 | +// Disable warnings for learning purposes and avoid useless `println!` usage only to use a variable |
| 4 | +#[allow(unused_variables)] |
| 5 | +#[allow(dead_code)] |
| 6 | +#[allow(unused_assignments)] |
| 7 | +#[allow(unused_mut)] |
| 8 | +#[allow(unreachable_code)] |
| 9 | + |
| 10 | +fn main() { |
| 11 | + /* Generic Types, Traits, and Lifetimes */ |
| 12 | + |
| 13 | + /* Generic Data Types */ |
| 14 | + // Function signatures or structs, that can use many different concrete data types. |
| 15 | + |
| 16 | + // Generic type parameters in `fn` definitions |
| 17 | + fn largest<T: PartialOrd>(list: &[T]) -> &T { |
| 18 | + let mut largest = &list[0]; |
| 19 | + for item in list { |
| 20 | + // Error: `>` cannot be applied to type `T` |
| 21 | + // Solution: `PartialOrd` trait (only use types whose values can be ordered) |
| 22 | + if item > largest { |
| 23 | + largest = item; |
| 24 | + } |
| 25 | + } |
| 26 | + largest |
| 27 | + } |
| 28 | + let number_list = vec![34, 50, 25, 100, 65]; |
| 29 | + let result = largest(&number_list); |
| 30 | + println!("The largest number is {}", result); |
| 31 | + |
| 32 | + let char_list = vec!['y', 'm', 'a', 'q']; |
| 33 | + let result = largest(&char_list); |
| 34 | + println!("The largest char is {}", result); |
| 35 | + |
| 36 | + // Generic type parameters in `struct` definitions |
| 37 | + struct Point<T> { |
| 38 | + x: T, |
| 39 | + y: T, |
| 40 | + } |
| 41 | + let integer = Point { x: 5, y: 10 }; |
| 42 | + let float = Point { x: 1.0, y: 4.0 }; |
| 43 | + |
| 44 | + // We can use different concrete types for each generic type parameter |
| 45 | + struct Point2<T, U> { |
| 46 | + x: T, |
| 47 | + y: U, |
| 48 | + } |
| 49 | + let both_integer = Point2 { x: 5, y: 10 }; |
| 50 | + let both_float = Point2 { x: 1.0, y: 4.0 }; |
| 51 | + let integer_and_float = Point2 { x: 5, y: 4.0 }; |
| 52 | + |
| 53 | + // Generic type parameters in `enum` definitions |
| 54 | + enum Option<T> { |
| 55 | + Some(T), |
| 56 | + None, |
| 57 | + } |
| 58 | + |
| 59 | + // Generic type parameters in `impl` blocks |
| 60 | + impl<T> Point<T> { |
| 61 | + fn x(&self) -> &T { |
| 62 | + &self.x |
| 63 | + } |
| 64 | + } |
| 65 | + let point = Point { x: 5, y: 10 }; |
| 66 | + println!("p.x = {}", point.x()); |
| 67 | + |
| 68 | + // We can also specify constraints on generic types when defining methods on the type. We could, for example, implement methods only on `Point<f32>` instances rather than on `Point<T>` instances. |
| 69 | + impl Point<f32> { |
| 70 | + fn distance_from_origin(&self) -> f32 { |
| 71 | + (self.x.powi(2) + self.y.powi(2)).sqrt() |
| 72 | + } |
| 73 | + } |
| 74 | + |
| 75 | + /* Traits: Defining Shared Behavior */ |
| 76 | + // Defines functionality a particular type has and can share with other types. |
| 77 | + // We can use trait bounds to specify that a generic type can be any type that has certain behavior. |
| 78 | + // Similar to interfaces in other languages. |
| 79 | + |
| 80 | + // Defining a Trait (using `trait`) |
| 81 | + /* Example: |
| 82 | + We have multiple structs that hold various kinds and amounts of text: a `NewsArticle` struct that holds a news story filed in a particular location and a `Tweet` that can have at most 140 characters along with metadata like whether it was a retweet or a reply to another tweet. |
| 83 | +
|
| 84 | + We want to make a media aggregator library crate named `aggregator` that can display summaries of data that might be stored in a `NewsArticle` or `Tweet` instance. |
| 85 | + */ |
| 86 | + pub trait Summary { |
| 87 | + fn summarize(&self) -> String; |
| 88 | + } |
| 89 | + |
| 90 | + // Implementing a Trait on a Type (using 'for`) |
| 91 | + pub struct NewsArticle { |
| 92 | + pub headline: String, |
| 93 | + pub location: String, |
| 94 | + pub author: String, |
| 95 | + pub content: String, |
| 96 | + } |
| 97 | + impl Summary for NewsArticle { |
| 98 | + fn summarize(&self) -> String { |
| 99 | + format!("{}, by {} ({})", self.headline, self.author, self.location) |
| 100 | + } |
| 101 | + } |
| 102 | + |
| 103 | + pub struct Tweet { |
| 104 | + pub username: String, |
| 105 | + pub content: String, |
| 106 | + pub reply: bool, |
| 107 | + pub retweet: bool, |
| 108 | + } |
| 109 | + impl Summary for Tweet { |
| 110 | + fn summarize(&self) -> String { |
| 111 | + format!("{}: {}", self.username, self.content) |
| 112 | + } |
| 113 | + } |
| 114 | + let tweet = Tweet { |
| 115 | + username: String::from("horse_ebooks"), |
| 116 | + content: String::from("of course, as you probably already know, people"), |
| 117 | + reply: false, |
| 118 | + retweet: false, |
| 119 | + }; |
| 120 | + println!("1 new tweet: {}", tweet.summarize()); |
| 121 | + |
| 122 | + /* Restriction: We can implement a trait on a type only if at least one of the trait or the type is local to our crate (coherence, and more specifically the orphan rule). |
| 123 | +
|
| 124 | + Examples: |
| 125 | + - We can implement standard library traits like `Display` on a custom type like `Tweet`. |
| 126 | + - We can also implement `Summary` on `Vec<T>`. |
| 127 | + - We can't implement external traits on external types. For example, we can't implement the `Display` trait on `Vec<T>` (both defined in the standard library). |
| 128 | + */ |
| 129 | + |
| 130 | + // Default Implementations |
| 131 | + pub trait SummaryWithDefault { |
| 132 | + fn summarize(&self) -> String { |
| 133 | + String::from("(Read more...)") |
| 134 | + } |
| 135 | + } |
| 136 | + |
| 137 | + // Traits as Parameters (using `impl Trait`) |
| 138 | + pub fn notify(item: &impl Summary) { |
| 139 | + println!("Breaking news! {}", item.summarize()); |
| 140 | + } |
| 141 | + |
| 142 | + // Trait Bound Syntax |
| 143 | + // The `impl Trait` syntax works for straightforward cases but is actually syntax sugar for a longer form known as a trait bound. |
| 144 | + pub fn notify_trait_bound<T: Summary>(item: &T) { |
| 145 | + println!("Breaking news! {}", item.summarize()); |
| 146 | + } |
| 147 | + // Trait bounds can express more complexity in others cases than `impl Trait` syntax. |
| 148 | + // Example: |
| 149 | + pub fn notify_verbose(item1: &impl Summary, item2: &impl Summary) {} |
| 150 | + // vs |
| 151 | + pub fn notify_trait_bound_verbose<T: Summary>(item1: &T, item2: &T) {} |
| 152 | + |
| 153 | + // Specifying Multiple Trait Bounds with the `+` Syntax |
| 154 | + pub fn notify_multiple(item: &(impl Summary + Display)) {} |
| 155 | + |
| 156 | + // Also valid with trait bounds |
| 157 | + pub fn notify_trait_bound_multiple<T: Summary + Display>(item: &T) {} |
| 158 | + |
| 159 | + // Clearer Trait Bounds with `where` Clauses |
| 160 | + fn some_function_verbose<T: Display + Clone, U: Clone + Debug>(t: &T, u: &U) -> i32 { |
| 161 | + 0 |
| 162 | + } |
| 163 | + // vs |
| 164 | + fn some_function<T, U>(t: &T, u: &U) -> i32 |
| 165 | + where |
| 166 | + T: Display + Clone, |
| 167 | + U: Clone + Debug, |
| 168 | + { |
| 169 | + 0 |
| 170 | + } |
| 171 | + |
| 172 | + // Returning Types that Implement Traits |
| 173 | + // Restriction: We can only return a single type. Example: we can't return a `NewsArticle` and a `Tweet` in the same function (even if both implements `Summary`). There is a way to do that covered in Chapter 17. |
| 174 | + fn returns_summarizable() -> impl Summary { |
| 175 | + Tweet { |
| 176 | + username: String::from("horse_ebooks"), |
| 177 | + content: String::from("of course, as you probably already know, people"), |
| 178 | + reply: false, |
| 179 | + retweet: false, |
| 180 | + } |
| 181 | + } |
| 182 | + |
| 183 | + // Using Trait Bounds to Conditionally Implement Methods |
| 184 | + // Example: |
| 185 | + struct Pair<T> { |
| 186 | + x: T, |
| 187 | + y: T, |
| 188 | + } |
| 189 | + impl<T> Pair<T> { |
| 190 | + fn new(x: T, y: T) -> Self { |
| 191 | + Self { x, y } |
| 192 | + } |
| 193 | + } |
| 194 | + impl<T: Display + PartialOrd> Pair<T> { |
| 195 | + fn compare_display(&self) { |
| 196 | + if self.x >= self.y { |
| 197 | + println!("The largest member is x = {}.", self.x); |
| 198 | + } else { |
| 199 | + println!("The largest member is y = {}.", self.y); |
| 200 | + } |
| 201 | + } |
| 202 | + } |
| 203 | + |
| 204 | + /* Validating References with Lifetimes */ |
| 205 | + // Lifetimes are another kind of generic. |
| 206 | + // Rather than ensuring that a type has the behavior we want, lifetimes ensure that references are valid as long as we need them to be. |
| 207 | + // Every reference in Rust has a lifetime, which is the scope for which that reference is valid. |
| 208 | + |
| 209 | + // Preventing Dangling References with Lifetimes |
| 210 | + let result; |
| 211 | + { |
| 212 | + let x = 5; |
| 213 | + result = &x; |
| 214 | + } |
| 215 | + // println!("result: {}", result); // error: `x` does not live long enough (once `x` goes out of scope, it will be deallocated and the memory will be invalid) |
| 216 | + |
| 217 | + // Generic Lifetimes in Functions |
| 218 | + // Lifetime Annotation Syntax |
| 219 | + // Lifetime annotations don't change how long any of the references live. |
| 220 | + // Rather, they describe the relationships of the lifetimes of multiple references to each other without affecting the lifetimes. |
| 221 | + |
| 222 | + // &i32 // a reference |
| 223 | + // &'a i32 // a reference with an explicit lifetime |
| 224 | + // &'a mut i32 // a mutable reference with an explicit lifetime |
| 225 | + |
| 226 | + // Example: Function that returns the longest of two string slices. |
| 227 | + // Signature express the following constraint: |
| 228 | + // The returned reference will be valid as long as both the parameters are valid. |
| 229 | + // => string slices that live at least as long as lifetime `'a`. |
| 230 | + fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { |
| 231 | + if x.len() > y.len() { |
| 232 | + x |
| 233 | + } else { |
| 234 | + y |
| 235 | + } |
| 236 | + } |
| 237 | + let string1 = String::from("abcd"); |
| 238 | + let string2 = "xyz"; |
| 239 | + let result = longest(&string1, string2); |
| 240 | + println!("The longest string is \"{}\".", result); |
| 241 | +} |
0 commit comments