通用結構
結構可以在一個或多個型別引數上成為通用的。當提到型別時,這些型別在 <>
中給出:
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::Display
的 Gen
例項定義。例如,如果我們嘗試傳入 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}
}
}
如果你在 Gen
的 T
上有型別邊界,那些也應該反映在 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