通用结构
结构可以在一个或多个类型参数上成为通用的。当提到类型时,这些类型在 <>
中给出:
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