泛型

泛型定义

我一直把泛型理解为类型系统中的参数，因为在 TS 中，类型和执行代码存在隔离。泛型定义就好像一个类型中的函数，通过传入参数得到具体的类型。同时，能通过泛型约束参数，返回值或结构体字段之间的关系.

Copy
fn get_value<T>(v: T) -> T {
v
}

enum Result<T, U> {
Ok(T),
Err(U)
}

struct List<T> {
items: Vec<T>,
current: T
}

impl<T> List<T> {
fn first(&self) -> T {
&self.items[0]
}
}

为结构体定义方法时也可以限定具体的类型实现类似重载的操作

Copy
impl List<i32> {
fn sum(&self) -> i32 {
let mut result = 0;
for item in &self.items {
result += item
}
result
}
}

// 指定 trait
impl<T: Display> List<T> {
fn sum(&self) -> i32 {}
}

Rust 的泛型代码不会影响性能，编译时会对其进行单态化, 也就是把使用到泛型的代码编译为具体类型的代码，有点像 Vue3 的静态优化.

Trait

这玩意有点绕啊

Trait 中文特征，跟 TS 对标类似 interface 或者 abstract class. 我理解为用来对泛型进行约定和限制，让某些抽象的泛型具象了一些，还可以拓展类型添加行为.

Copy
trait Desc {
fn read(&self) -> &str;
fn default() -> &str {
"default impl"
}
}

impl<T> Desc for List<T> {
fn read(&self) -> &str {
"a list"
}
}

实现 Trait 受相干性限制，实现方或者被实现方至少有一个在本地作用域才能进行，这条规则可以避免其他人在 crate 以外破坏代码.

抽象之上的抽象实现 blank implementations.

Copy
// 为所有实现过 Display 的类型实现 ToString
impl<T: Display> ToString for T {}

定义后就可以进行约束了

Copy
fn get_desc(target: &impl Desc) {}

// trait bound
fn print_desc<T: Desc + Display>(target: &T) -> impl Display {}

fn print_default_desc<T>(target: &T) where T: Desc + Display {}

生命周期

生命周期的存在是为了保证引用有效，避免悬垂引用。因为所有权的限制，被引用的对象不能比引用者存在时间短，我们不可能在函数体内创建一个引用然后返回，否则离开作用域时源数据被销毁，返回的引用将成为悬垂引用。所以如果一个函数返回了引用，则引用一定与参数中的引用有关联。这个关联有时候无法通过代码推断，比如传入两个引用返回了一个引用，这时就需要我们去明确指定生命周期辅助编译器完成推断.

Copy
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}

// 根据逻辑不需要为 y 指定生命周期
fn first<'a>(x: &'a str, y: &str) -> &'a str {
x
}

struct Person<'a> {
name: &'a str
}

// 静态生命周期, 有点像 any, 使用前要保证其能存在整个程序运行过程
let s:&'static str = "hello"

省略规则

Rust 内置了一些可预测的模式减少重复代码，如果在这些规则之后仍存在无法推断的生命周期，就需要我们明确指定.

每个引用的生命周期必须与至少一个输入引用的生命周期相同。

如果只有一个输入引用，那么输出引用的生命周期与输入引用的生命周期相同。

如果有多个输入引用，其中一个是 &self 或 &mut self，那么输出引用的生命周期与该输入引用的生命周期相同。

如果有多个输入引用，其中一个是 &self，那么输出引用的生命周期与另一个输入引用的生命周期相同。