泛型定義#
我一直把泛型理解為類型系統中的參數,因為在 TS 中,類型和執行代碼存在隔離。泛型定義就好像一個類型中的函數,通過傳入參數得到具體的類型。同時,能通過泛型約束參數,返回值或結構體字段之間的關係。
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]
}
}
為結構體定義方法時也可以限定具體的類型實現類似重載的操作
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
。我理解為用來對泛型進行約定和限制,讓某些抽象的泛型具象了一些,還可以拓展類型添加行為。
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.
// 為所有實現過 Display 的類型實現 ToString
impl<T: Display> ToString for T {}
定義後就可以進行約束了
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 {}
生命周期#
生命周期的存在是為了保證引用有效,避免懸垂引用。因為所有權的限制,被引用的對象不能比引用者存在時間短,我們不可能在函數體內創建一個引用然後返回,否則離開作用域時源數據被銷毀,返回的引用將成為懸垂引用。所以如果一個函數返回了引用,則引用一定與參數中的引用有關聯。這個關聯有時候無法通過代碼推斷,比如傳入兩個引用返回了一個引用,這時就需要我們去明確指定生命周期輔助編譯器完成推斷。
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 內置了一些可預測的模式減少重複代碼,如果在這些規則之後仍存在無法推斷的生命周期,就需要我們明確指定。