Rust - Basic - 10 - Generaic Data Type & Traits & Lifetime

Generic: Data Type & Traits & Lifetime

Compréhension

  • Generic 1 : Data Type

    // Types génériques声明 - struct
    struct Point<T> {
        x: T,
        y: T,
    }
    let point = Point { x: 5, y: 4 }; // 必须是同一type
    struct PointDiffType<T, U> {
        x: T,
        y: U,
    }
    let point_diff_type = PointDiffType{ x: 5, y: 4.0 }; // 不同type
    
    // Types génériques声明 - impl
    impl<T> Point<T> {
        fn x(&self) -> &T {
            &self.x
        }
    }
    
    // Types génériques声明 - enum
    enum Option<T> {
        Some(T),
        None,
    }
    enum Result<T, E> {
        Ok(T),
        Err(E),
    }
    
    // 一种 mix-up 实现
    struct Point<T, U> {
        x: T,
        y: U,
    }
    impl<T, U> Point<T, U> {
    		// 由于Types génériques的高灵活性,这里做了巧妙的 field 交叉混合,组合出第三个新Types génériques实例
        // 这种语法可用于 middleware
        fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
            Point {
                x: self.x,
                y: other.y,
            }
        }
    }
    fn main() {
        let p1 = Point { x: 5, y: 10.4 };
        let p2 = Point { x: "Hello", y: 'c' };
        let p3 = p1.mixup(p2);
        println!("p3.x = {}, p3.y = {}", p3.x, p3.y);
    }
    
  • Types génériques,

    ,Monomorphization process Types génériquestype

    // 比如使用了 Some Types génériques代码
    let integer = Some(5);
    let float = Some(5.0);
    // 编译期会 Monomorphization 为以下代码
    enum Option_i32 {
        Some(i32),
        None,
    }
    enum Option_f64 {
        Some(f64),
        None,
    }
    fn main() {
        let integer = Option_i32::Some(5);
        let float = Option_f64::Some(5.0);
    }
    
  • Generic 2 :Traits & Trait Bounds

    Trait “Traits”,, struct、fonctionparamètre、fonctionvaleur de retour Traits

    struct,Traits struct Traits method, Java interface

    impl structA for traitA

    generic type,Traits: concrete type Traits( trait bound),fonction

    impl<T: traitA + traitB + ...> structA<T> {...}

    fonctionparamètre,Traits:Traitsparamètre,fonctionparamètre()

    pub fn notify(item: &impl Summary) {...}

    fonctionvaleur de retour,Traits:valeur de retourTraits

    // 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)
        }
    }
    
    // Default Implementations
    pub trait Summary {
        fn summarize(&self) -> String {
            String::from("(Read more...)")
        }
    }
    
    // Default Implementations 可以appel其它 impl
    pub trait Summary {
        fn summarize_author(&self) -> String;
    
        fn summarize(&self) -> String {
            format!("(Read more from {}...)", self.summarize_author())
        }
    }
    
    // Traits as Parameters
    pub fn notify(item: &impl Summary) {
        println!("Breaking news! {}", item.summarize());
    }
    
    // 可以使用 generic type 来指定 trait bounds
    pub fn notify<T: Summary>(item1: &T, item2: &T) {}
    
    // 多个 trait 的情况 1 - 使用 + 操作符
    pub fn notify(item: &(impl Summary + Display)) {
    pub fn notify<T: Summary + Display>(item: &T) {
    
    // 多个 trait 的情况 2 - 使用 where
    fn some_function<T, U>(t: &T, u: &U) -> i32
        where T: Display + Clone,
              U: Clone + Debug
    
    // Returning Types that Implement Traits
    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,
        }
    }
    
    // 可以通过 trait bounds 特性,以实现:有对应 trait 的 generic type,才能有对应的 method
    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 }
        }
    }
    
    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);
            }
        }
    }
    
  • Generic 3 :Lifetimes

    &i32        // a reference
    &'a i32     // a reference with an explicit lifetime
    &'a mut i32 // a mutable reference with an explicit lifetime
    // 一般用在 function 定义时
    fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
        if x.len() > y.len() {
            x
        } else {
            y
        }
    }
    // 可用于 struct
    struct ImportantExcerpt<'a> {
        part: &'a str,
    }
    
    // Lifetime Elision - 活用 3 条规则
    fn first_word(s: &str) -> &str {...}
    
    // static 生命期,可以在整个程序运行时保持可用
    let s: &'static str = "I have a static lifetime.";
    

    ** 3 ,, lifetimes references**

    The first rule is that each parameter that is a reference gets its own lifetime parameter.

    :paramètreparamètre。

    The second rule is if there is exactly one input lifetime parameter, that lifetime is assigned to all output lifetime parameters

    :paramètre,paramètreparamètre。

    The third rule is if there are multiple input lifetime parameters, but one of them is &self or &mut self because this is a method, the lifetime of self is assigned to all output lifetime parameters.

    :paramètre, &self &mut self(method), self paramètreparamètre。

  • 3

    use std::fmt::Display;
    
    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
        }
    }
    

Origin

https://doc.rust-lang.org/book/ch10-00-generics.html

…

Performance of Code Using Generics

You might be wondering whether there is a runtime cost when you’re using generic type parameters. The good news is that Rust implements generics in such a way that your code doesn’t run any slower using generic types than it would with concrete types.

Rust accomplishes this by performing monomorphization of the code that is using generics at compile time. Monomorphization is the process of turning generic code into specific code by filling in the concrete types that are used when compiled.

…

The compiler uses three rules to figure out what lifetimes references have when there aren’t explicit annotations. The first rule applies to input lifetimes, and the second and third rules apply to output lifetimes. If the compiler gets to the end of the three rules and there are still references for which it can’t figure out lifetimes, the compiler will stop with an error. These rules apply to fn definitions as well as impl blocks.

The first rule is that each parameter that is a reference gets its own lifetime parameter. In other words, a function with one parameter gets one lifetime parameter: fn foo<'a>(x: &'a i32); a function with two parameters gets two separate lifetime parameters: fn foo<'a, 'b>(x: &'a i32, y: &'b i32); and so on.

The second rule is if there is exactly one input lifetime parameter, that lifetime is assigned to all output lifetime parameters: fn foo<'a>(x: &'a i32) -> &'a i32.

The third rule is if there are multiple input lifetime parameters, but one of them is &self or &mut self because this is a method, the lifetime of self is assigned to all output lifetime parameters. This third rule makes methods much nicer to read and write because fewer symbols are necessary.