rust generic types, traits and lifetimes Generic data types // in function definition fn largest<T: PartialOrd + Copy>(list: &[T]) -> T { let mut largest = list[0]; for &item in list { if item > largest { largest = item; } } largest } fn main() { let number_list = vec![34, 50, 25, 100, 65]; let result = largest(&number_list); println!("The largest number is {}", result); let char_list = vec!['y', 'm', 'a', 'q']; let result = largest(&char_list); println!("The largest char is {}", result); } // in struct definition struct Point<T> { x: T, y: T, } // in method definition impl<T> Point<T> { fn x(&self) -> &T { &self.x } } fn main() { let integer = Point { x: 5, y: 10 }; let float = Point { x: 1.0, y: 4.0 }; let p = Point { x: 5, y: 10 }; println!("p.x = {}", p.x()); } // in enum definition enum Option<T> { Some(T), None, } Traits: defining shared behavior // defining a trait pub trait Summary { fn summarize(&self) -> String; } // implementing a trait on a Type 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) } } // default implementation pub trait Summary { 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 {} 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) } } // trait as parameters pub fn notify(item: &impl Summary) { println!("Breaking news! {}", item.summarize()); } // or using trait bound syntax pub fn notify<T: Summary>(item: &T) { println!("Breaking news! {}", item.summarize()); } // specifying multiple trait bounds with the + syntax pub fn notify<T: Summary + Display>(item: &T) { // hard to read signature fn some_function<T: Display + Clone, U: Clone + Debug>(t: &T, u: &U) -> i32 { // clearer trait bounds with `where` clauses fn some_function<T, U>(t: &T, u: &U) -> i32 where T: Display + Clone, U: Clone + Debug { // returning types that implement traits // /!\ only possible to return a single type! // e.g. we cannot return either a `NewsArticle` or a `Tweet` fn returns_summarizable() -> impl Summary { Tweet { username: String::from("horse_ebooks"), content: String::from( "of course, as you probably already know, people", ), reply: false, retweet: false, } } use std::fmt::Display; struct Pair<T> { x: T, y: T, } impl<T> Pair<T> { fn new(x: T, y: T) -> Self { Self { x, y } } } // conditionally implement methods on a generic type depending on trait bounds impl<T: Display + PartialOrd> Pair<T> { fn cmp_display(&self) { if self.x >= self.y { println!("The largest member is x = {}", self.x); } else { println!("The largest member is y = {}", self.y); } } } Validating references with lifetimes // lifetime annotation syntax &i32 // a reference &'a i32 // a reference with an explicit lifetime &'a mut i32 // a mutable reference with an explicit lifetime fn main() { let string1 = String::from("abcd"); let string2 = "xyz"; let result = longest(string1.as_str(), string2); println!("The longest string is {}", result); } // lifetime annotations in function signatures fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } // lifetime annotations in struct definitions struct ImportantExcerpt<'a> { part: &'a str, } fn main() { let novel = String::from("Call me Ishmael. Some years ago..."); let first_sentence = novel.split('.').next().expect("Could not find a '.'"); let i = ImportantExcerpt { part: first_sentence, }; } // lifetime elision // same lifetime annotation => predictable => infer lifetime fn first_word(s: &str) -> &str { let bytes = s.as_bytes(); for (i, &item) in bytes.iter().enumerate() { if item == b' ' { return &s[0..i]; } } &s[..] } // lifetime annotation in method definition struct ImportantExcerpt<'a> { part: &'a str, } impl<'a> ImportantExcerpt<'a> { fn level(&self) -> i32 { 3 } } impl<'a> ImportantExcerpt<'a> { fn announce_and_return_part(&self, announcement: &str) -> &str { println!("Attention please: {}", announcement); self.part } } fn main() { let novel = String::from("Call me Ishmael. Some years ago..."); let first_sentence = novel.split('.').next().expect("Could not find a '.'"); let i = ImportantExcerpt { part: first_sentence, }; } // static lifetime: reference can live for the entire duration of the program let s: &'static str = "I have a static lifetime.";