泛型(Generics)#
泛型(Generics) 讓我們用 型別參數 撰寫能適用於多種型別的程式碼,不需要為每個型別重複寫相同邏輯。
你其實已經用過泛型了:Vec<T>、HashMap<K, V>、Option<T>、Result<T, E> 都是泛型型別。本章說明如何自己定義泛型。
1. 為什麼需要泛型#
假設我們想找出清單中的最大值,整數和浮點數各需要一個版本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| fn largest_i32(list: &[i32]) -> i32 {
let mut largest = list[0];
for &item in list {
if item > largest { largest = item; }
}
largest
}
fn largest_f64(list: &[f64]) -> f64 {
let mut largest = list[0];
for &item in list {
if item > largest { largest = item; }
}
largest
}
|
邏輯完全相同,只差型別。泛型讓我們只寫一次:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
let mut largest = list[0];
for &item in list {
if item > largest { largest = item; }
}
largest
}
fn main() {
let numbers = vec![34, 50, 25, 100, 65];
println!("最大值:{}", largest(&numbers)); // 100
let chars = vec!['y', 'm', 'a', 'q'];
println!("最大值:{}", largest(&chars)); // y
}
|
T: PartialOrd + Copy 是 型別邊界(Trait Bound) ,要求 T 支援比較(>)且可複製。型別邊界的完整說明見 CH15 特徵。
2. 泛型函數#
在函數名稱後用 <T> 宣告型別參數,參數和回傳值都可以使用 T:
1
2
3
4
5
6
7
8
9
10
11
| fn first<T>(list: &[T]) -> &T {
&list[0]
}
fn main() {
let numbers = vec![1, 2, 3];
let words = vec!["hello", "world"];
println!("{}", first(&numbers)); // 1
println!("{}", first(&words)); // hello
}
|
可以宣告多個型別參數:
1
2
3
4
5
6
7
8
| fn pair<T, U>(first: T, second: U) -> (T, U) {
(first, second)
}
fn main() {
let p = pair(42, "hello");
println!("{} {}", p.0, p.1); // 42 hello
}
|
3. 泛型結構體#
1
2
3
4
5
6
7
8
9
| struct Point<T> {
x: T,
y: T,
}
fn main() {
let int_point = Point { x: 5, y: 10 };
let float_point = Point { x: 1.0, y: 4.0 };
}
|
若 x 和 y 需要不同型別,使用兩個型別參數:
1
2
3
4
5
6
7
8
| struct Point<T, U> {
x: T,
y: U,
}
fn main() {
let mixed = Point { x: 5, y: 4.0 }; // x 是 i32,y 是 f64
}
|
4. 泛型枚舉#
Option<T> 和 Result<T, E> 就是泛型枚舉:
1
2
3
4
5
6
7
8
9
10
| // 標準函式庫中的定義(概念等同)
enum Option<T> {
Some(T),
None,
}
enum Result<T, E> {
Ok(T),
Err(E),
}
|
現在你理解了為何 Option<i32> 表示「可能有一個 i32」:T 在使用時被替換成 i32。
5. 泛型方法(impl<T>)#
為泛型結構體實作方法時,impl 後面也要加 <T>:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
fn main() {
let p = Point { x: 5, y: 10 };
println!("x = {}", p.x()); // 5
}
|
也可以只為特定型別實作額外的方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| struct Point<T> {
x: T,
y: T,
}
impl Point<f64> {
fn distance_from_origin(&self) -> f64 {
(self.x * self.x + self.y * self.y).sqrt()
}
}
fn main() {
let p = Point { x: 3.0, y: 4.0 };
println!("距離原點:{}", p.distance_from_origin()); // 5
}
|
6. 泛型的零成本抽象#
Rust 的泛型在編譯時進行 單型化(Monomorphization) :編譯器找出所有實際使用的型別,產生對應的具體程式碼。
1
2
3
4
5
6
7
| // 你寫的
let v1: Vec<i32> = vec![1, 2, 3];
let v2: Vec<f64> = vec![1.0, 2.0];
// 編譯器實際產生兩個具體版本(概念上)
// Vec_i32 的實作
// Vec_f64 的實作
|
因此泛型 不會有執行時的效能損失 ,與手動為每個型別撰寫程式碼完全等效。
Reference#
https://doc.rust-lang.org/book/ch10-01-syntax.html