Rust: Generic Types, Traits, and Lifetimes

Generic Types, Traits, and Lifetimes

Removing Duplication by Extracting a Function

如果代码需要重复使用,考虑封装成函数

Generic Data Types

泛型:提高代码的复用性

函数泛型格式

fn fn_name<T,...>(paraments...) -> type {
    
}

可以看到只需要加<T…> 即可

fn max_element<T>(item: &[T]) -> T {
    let mut val = item[0];
    for &x in item {
        if x > val {
            val = x;
        }
    }
    return val;
}

// binary operation `>` cannot be applied to type `T
// 这个暂时不管

结构体泛型

格式

struct name<T..> {
    name1: T,
    ...
}
struct Point<T> {
    x: T,
    y: T,
}

枚举中的泛型

Option<T>Result<T, E>

方法泛型

impl<T> Point<T> {// T 要 impl<T>
    fn x(&self) -> &T {
        &self.x
    }
}

impl Point<i32> { // 具体类型 无 <T>
    fn y(&self) -> &i32 {
        &self.y
    }
}
struct Point<T, U> {
    x: T,
    y: U,
}

impl<T, U> Point<T, U> {
    fn mixed<V, W>(self, p: Point<V, W>) -> Point<T, W> {
        Point {
            x: self.x,
            y: p.y,
        }
    }
} 

Traits: Defining Shared Behavior

定义trait,使用关键字 trait

pub trait Summary {
    fn summary(&self) -> String;
}

只有函数签名,无具体实现,也可以默认实现

实现 trait

格式

impl trait_name for struct_name {
    ...
}
// lib
pub trait Summary {
    fn summary(&self) -> String;
}

pub struct Data {
    pub username: String,
}

impl Summary for Data {
    fn summary(&self) -> String {
        format!("{}", self.username)
    }
}


// main
use rust::Data;// 引入 //[package] name = "rust"
use rust::Summary;

fn main() {
    let x = Data {
        username: String::from("SCUT"),
    };
    println!("{}", x.summary());
}

实现 trait 的约束

这个 类型 或 这个 trait 是本地 crate 里定义的

使用 trait 作为参数

impl trait 语法,适用于简单情况

fn test(x: impl Summary) {
}

泛型写法

fn test<T: Summary>(x: T) {
}

+ 添加多个 trait

fn test<T: Summary + Display>(x: T) {
}

如果有多个量需要不同 trait

fn test<T: Summary + Display, U: Clone>(x: T, y: U) {
}

where 语句

fn test<T, U>(x: T, y: U) -> () where 
    T: Summary + Display,
    U: Clone, 
{}

trait 作为返回类型, 只能返回同一种类型

fn test() -> impl Summary {
    Data {
        username: String::from("SCUT"),
    }
}

Validating References with Lifetimes

看一个例子

fn main() {
    let mut r;
    {
        let x = 5;
        r = &x;
    }
    println!("{}", r);
}

// borrowed value does not live long enough

x 的生命周期小于 r,引用的对象生命周期必须大于等于引用者

生命周期参数名:以 开头,通常全小写,一般用 ’a

生命周期标注位置:在引用的 & 后面,使用空格将标注和引用类型分开

&i32 // 引用
& 'a i32 // 带有显示生命周期的引用
& 'a mut i32 // 带有显示生命周期的可变引用

例如

fn longest(str1: &String, str2: &String) -> &String {
    if str1.len() > str2.len() {
        str1
    } else {
        str2
    }
}

fn main() {
    let s = String::from("0");
    let t = String::from("2024");
    println!("{}", longest(&s, &t));
}

// expected named lifetime parameter

无法判断 str1 和 str2 的生命周期长度

更改为

fn longest<'a>(str1: &'a String, str2: &'a String) -> &'a String {
    if str1.len() > str2.len() {
        str1
    } else {
        str2
    }
}

fn main() {
    let s = String::from("0");
    let t = String::from("2024");
    println!("{}", longest(&s, &t));
}

这样生命周期会取较小的那个,实际的生命周期未改变

结构体与生命周期

struct Ie<'a> {
    part: &'a str,
}

fn main() {
    let s = "2024";
    let i = Ie {
        part: s,
    };
}

// 字段的生命周期要大于等于对象本身的生命周期

生命周期的省略规则

所有引用都有生命周期,需要为使用生命周期的函数或 struct 指定生命周期参数

如果应用规则后,引用的生命周期仍模糊不清 → 编译错误,解决办法:添加生命周期标注,表明引用间的相互关系

输入、输出生命周期

函数/方法的参数:输入生命周期

函数/方法的返回值:输出生命周期

三个规则

  1. 每个引用类型的参数都有自己的生命周期
  2. 如果只有一个输入生命周期参数,那么该生命周期会被赋给所有输出生命周期
  3. 如果有多个输入生命周期,但其中一个是 &self 或 &mut self (方法),那么 self 的生命周期会被赋给所有输出生命周期参数、

在应用三个规则后,输出生命周期还不清楚,编译错误

方法中的生命周期标注

和泛型类似

struct Ie<'a> {
    part: &'a str,
}

impl<'a> Ie<'a> {
    fn lv(&self) -> i32 {
        0
    }

    fn rt(&self, s: & str) ->&str {
        self.part
    }// 应用规则三
}

静态生命周期

'static

所有字符串字面值都拥有 static 生命周期

let s:&'static str = "2024";

生命周期与 trait bound

fn test<'a, T>(s: &'a str, t: &'a str, z: T) -> &'a str 
where
    T: Display,
{
    println!("{}", z);
    if s.len() < t.len() {
        t
    } else {
        s
    }
}