泛型

有時我們希望函式或資料型別能接受不同型別的參數,在 Rust 中提供了泛型來滿足這項需求。 在型別論中,泛型又被稱為「參數式多型」,係指型別或函式面對給定的參數(parametric)時具有多種型態(「poly」為多,「morph」指型態)。

總之不管理論,先來看一些泛型程式,Rust 標準函式庫提供了型別 Option<T> 即是泛型:

enum Option<T> {
    Some(T),
    None,
}

其中我們曾見過數次的 <T> 即表示泛型資料型別,在 enum 的宣告中只要遇到 T 即會被替換成泛型中使用的型別。 以下是使用 Option<T> 並附加額外型別註釋的範例:

let x: Option<i32> = Some(5);

在型別宣告中,我們使用與 Option<T> 相似的 Option<i32>。 因此在這個 Option 中,T 即是 i32。 等式右邊我們加上 T5Some(T)。 因為 5 屬於型別 i32,所以兩邊相符,可以通過編譯。 若兩邊不符則會發生錯誤:

let x: Option<f64> = Some(5);
// error: mismatched types: expected `core::option::Option<f64>`,
// found `core::option::Option<_>` (expected f64 but found integral variable)

這並不表示我們不能讓 Option<T> 對應為 f64,只是等式兩邊必須相符:

let x: Option<i32> = Some(5);
let y: Option<f64> = Some(5.0f64);

這樣就行了。 一個定義,多種使用方式。

泛型並非只能泛型一個型別。 讓我們看看 Rust 標準函式庫的另一個型別 Result<T, E>

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

這是個針對 TE 兩個 型別泛型的型別。 型別的大寫字可以是任何字。 想要的話,我們也可以把 Result<T, E> 定義成:

enum Result<A, Z> {
    Ok(A),
    Err(Z),
}

慣例上第一個泛型參數會是 T 表示「type」,然後我們用 E 表示「error」。 然而 Rust 實際上並不管這些東西。

型別 Result<T, E> 用在回傳運算結果,也能夠在出錯時回傳錯誤。

泛型函式

我們可以用類似的語法宣告接受泛型的函式:

fn takes_anything<T>(x: T) {
    // do something with x
}

語法包含兩個部分:<T> 表示 "這個函式對型別 T 泛型",而 x: T 表示 "x 的型別為 T"

一個泛型可以用在多個參數上:

fn takes_two_of_the_same_things<T>(x: T, y: T) {
    // ...
}

我們也可以宣告接受多個泛型的版本:

fn takes_two_things<T, U>(x: T, y: U) {
    // ...
}

泛型結構體

泛型也可以使用在 struct 中:

struct Point<T> {
    x: T,
    y: T,
}

let int_origin = Point { x: 0, y: 0 };
let float_origin = Point { x: 0.0, y: 0.0 };

與函式很類似,<T> 宣告泛型參數,使用 x: T 宣告型別。

當要加上泛型結構體的實作時,在 impl 之後宣告型別參數:

# struct Point<T> {
#     x: T,
#     y: T,
# }
#
impl<T> Point<T> {
    fn swap(&mut self) {
        std::mem::swap(&mut self.x, &mut self.y);
    }
}

目前為止,我們已經展示泛型可以用於任意型別。 這在許多狀況下非常有用,例如先前看到的 Option<T>,以及接下來會看到通用容器型別,如 Vec<T>。 另一方面,常常我們想要用這樣的彈性換取表現能力, 參考 trait bounds 了解如何做到。

commit 6ba9520