通用結構

結構可以在一個或多個型別引數上成為通用的。當提到型別時,這些型別在 <> 中給出:

struct Gen<T> {
    x: T,
    z: isize,
}

// ...
let _: Gen<bool> = Gen{x: true, z: 1};
let _: Gen<isize> = Gen{x: 42, z: 2};
let _: Gen<String> = Gen{x: String::from("hello"), z: 3};

可以使用逗號給出多種型別:

struct Gen2<T, U> {
    x: T,
    y: U,
}

// ...
let _: Gen2<bool, isize> = Gen2{x: true, y: 42};

型別引數是型別的一部分,因此具有相同基本型別但具有不同引數的兩個變數不可互換:

let mut a: Gen<bool> = Gen{x: true, z: 1};
let b: Gen<isize> = Gen{x: 42, z: 2};
a = b; // this will not work, types are not the same
a.x = 42; // this will not work, the type of .x in a is bool

如果你想編寫一個接受 struct 的函式而不管它的型別引數賦值,那麼該函式也需要是通用的:

fn hello<T>(g: Gen<T>) {
    println!("{}", g.z); // valid, since g.z is always an isize
}

但是如果我們想要編寫一個能夠始終列印 g.x 的功能呢?我們需要將 T 限制為可以顯示的型別。我們可以用型別邊界來做到這一點:

use std::fmt;
fn hello<T: fmt::Display>(g: Gen<T>) {
    println!("{} {}", g.x, g.z);
}

hello 函式現在僅為 T 型別實現 fmt::DisplayGen 例項定義。例如,如果我們嘗試傳入 Gen<(bool, isize)>,編譯器會抱怨沒有為該型別定義 hello

我們也可以直接在 struct 的型別引數上使用型別邊界來表示你只能為某些型別構造 struct

use std::hash::Hash;
struct GenB<T: Hash> {
    x: T,
}

任何有權訪問 GenB 的函式現在都知道 x 的型別實現了 Hash,因此他們可以呼叫 .x.hash()。可以通過用+分隔它們來給出相同引數的多個型別邊界。

與函式相同,可以使用 where 關鍵字在 <> 之後放置型別邊界:

struct GenB<T> where T: Hash {
    x: T,
}

這具有相同的語義含義,但是當你有複雜的邊界時,可以使簽名更容易閱讀和格式化。

型別引數也可用於 struct 的例項方法和相關方法:

// note the <T> parameter for the impl as well
// this is necessary to say that all the following methods only
// exist within the context of those type parameter assignments
impl<T> Gen<T> {
    fn inner(self) -> T {
        self.x
    }
    fn new(x: T) -> Gen<T> {
        Gen{x: x}
    }
}

如果你在 GenT 上有型別邊界,那些也應該反映在 impl 的型別範圍內。如果型別滿足特定屬性,你還可以使 impl 邊界更緊密地說只有給定方法存在:

impl<T: Hash + fmt::Display> Gen<T> {
    fn show(&self) {
        println!("{}", self.x);
    }
}

// ...
Gen{x: 42}.show(); // works fine
let a = Gen{x: (42, true)}; // ok, because (isize, bool): Hash
a.show(); // error: (isize, bool) does not implement fmt::Display