Lv777

Lv777

github
twitter
jike
playstation
xiaoyuzhou

泛型

泛型定義#

我一直把泛型理解為類型系統中的參數,因為在 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 內置了一些可預測的模式減少重複代碼,如果在這些規則之後仍存在無法推斷的生命周期,就需要我們明確指定。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。