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.";