// trait defines functionalitity a particular type has and // can share with other types. Can use traits to define shared // behavior in an abstract way. Can use trait bounds to specify // that a generic type can be any type that has certain behavior use std::fmt::Display; // Like Haskell's deriving // Except orphans aren't allowed at all pub trait Summary { // fn summarize(&self) -> String; // Structs must provide own implementation fn summarize(&self) -> String { String::from("(Read more...)") } } 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) } } // Should use lifetime annotations when using a reference // in a variable struct ImportantExcerpt<'a> { part: &'a str, } 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()); // The next lines fail as borrowing // let r; // { // let x = 5; // r = &x; // } // println!("r: {}", r); let x = 5; let r = &x; println!("r: {r}"); let string1 = String::from("abcd"); let string2 = "xyz"; // need a lifetime reference for situations like this // &i32: a regular reference // &'a i32: a reference with an explicit lifetime // &'a mut i32: a mutable reference with an explicit lifetime let result = longest(string1.as_str(), string2); println!("The longest string is {result}"); let string1 = String::from("long string is long"); // let result; { let string2 = String::from("xyz"); let result = longest(string1.as_str(), string2.as_str()); // result = longest(string1.as_str(), string2.as_str()); println!("The longest string is {result}"); } // println!("The longest string is {result}"); // the static lifetime // Can life for the duration of the program let s: &'static str = "I have a static lifetime"; } // fn notify(item1: &T, item2: &T) {} fn notify(item: &(impl Summary + std::fmt::Display)) {} fn notify2(item: &T) {} fn some_function(t: &T, u: &U) -> i32 where // Similar to haskell :) T: std::fmt::Display + Clone, U: Clone + std::fmt::Debug, { 2 } // impl ToString for T {} // <'a> and 'a dispersed in the function parameters are // lifetime annotations. Lifetimes annotations are needed because // Rust can't tell whether the reference being returned refers to x or y // Feels like a similar vibe to Haskell `forall a`. fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } // lifetime elision rules. Some functions that have common patterns // with references passed as a parameter have lifetime annotations // passed implicitly due to soooooo many functions having this // lifetime annotation pattern used. // // Lifetimes on function/method parameters called input lifetimes. // Lifetimes on return values output lifetimes // // 3 Rules for lifetime functions // 1: Compiler assigns different lifetime parameter to each lifetime // in each input type // fn foo(x: &i32) becomes fn foo<'a>(x: &'a i32) // fn foo(x: &i32, y: &i32) becomes fn foo<'a, 'b>(x: &'a i32, y: &'b i32) // fn foo(x: &ImportantExcerpt) becomes fn foo<'a, 'b>(x: &'a ImportantExcerpt<'b>) // 2: If exactly one input lifetime paramter, lifetime applied to all output // lifetime parameters // 3: If multiple input lifetime parameters, but one of them is &self or &mut self, // lifetime of self assigned to all output lifetime parameters // Lifetime annotations in method definitions impl<'a> ImportantExcerpt<'a> { fn level(&self) -> i32 { 3 } // example of third lifetime elision rule fn announce_and_return_part(&self, announcement: &str) -> &str { println!("Attention Please: {announcement}"); self.part } } // Generic Type Paramters, trait bounds, and lifetimes together fn longest_with_an_announcement<'a, T>(x: &'a str, y: &'a str, ann: T) -> &'a str where T: Display, { println!("Announcement!: {ann}"); if x.len() > y.len() { x } else { y } }