通用结构

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

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