在为结构体和枚举实现方法时(像第五章那样),一样也可以用泛型。定义的结构体 Point,和在其上实现的名为 x 的方法。
x: T,
y: T,
}
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
fn main() {
let p = Point { x: 5, y: 10 };
println!("p.x = {}", p.x());
}
这里在 Point 上定义了一个叫做 x 的方法来返回字段 x 中数据的引用:
注意必须在 impl 后面声明 T,这样就可以在 Point 上实现的方法中使用它了。在 impl 之后声明泛型 T ,这样 Rust 就知道 Point 的尖括号中的类型是泛型而不是具体类型。因为再次声明了泛型,我们可以为泛型参数选择一个与结构体定义中声明的泛型参数所不同的名称,不过依照惯例使用了相同的名称。
impl 中编写的方法声明了泛型类型可以定位为任何类型的实例,不管最终替换泛型类型的是何具体类型。
另一个选择是定义方法适用于某些有限制(constraint)的泛型类型。例如,可以选择为 Point 实例实现方法,而不是为泛型 Point 实例。了一个没有在 impl 之后(的尖括号)声明泛型
的例子,这里使用了一个具体类型,f32:
# x: T,
# y: T,
# }
#
# impl<T> Point<T> {
# fn x(&self) -> &T {
# &self.x
# }
# }
#
impl Point<f32> {
fn distance_from_origin(&self) -> f32 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
#
# fn main() {
# let p = Point { x: 5, y: 10 };
#
# println!("p.x = {}", p.x());
# }
构建一个只用于拥有泛型参数 T 的结构体的具体类型的 impl 块
这段代码意味着 Point 类型会有一个方法distance_from_origin,而其他 T 不是 f32 类型的Point 实例则没有定义此方法。这个方法计算点实例与坐标 (0.0, 0.0) 之间的距离,并使用了只能用于浮点型的数学运算符。
结构体定义中的泛型类型参数并不总是与结构体方法签名中使用的泛型是同一类型。Point 结构体使用了泛型类型 X1 和 Y1,为 mixup 方法签名使用了 X2 和 Y2 来使得示例更加清楚。这个方法用 self 的 Point 类型的 x 值(类型 X1)和参数的 Point 类型的 y 值(类型 Y2)来创建一个新 Point类型的实例:
x: X1,
y: Y1,
}
impl<X1, Y1> Point<X1, Y1> {
fn mixup<X2, Y2>(self, other: Point<X2, Y2>) -> Point<X1, Y2> {
Point {
x: self.x,
y: other.y,
}
}
}
fn main() {
let p1 = Point { x: 5, y: 10.4 };
let p2 = Point { x: "Hello", y: 'c' };
let p3 = p1.mixup(p2);
println!("p3.x = {}, p3.y = {}", p3.x, p3.y);
}
在 main 函数中,定义了一个有 i32 类型的 x(其值为 5)和 f64 的 y(其值为 10.4)的 Point。p2 则是一个有着字符串 slice 类型的 x(其值为 ”Hello”)和 char 类型的 y(其值为c)的 Point。在 p1 上以 p2作为参数调用 mixup 会返回一个 p3,它会有一个 i32 类型的 x,因为 x 来自 p1,并拥有一个 char 类型的 y,因为 y 来自 p2。println! 会打印出 p3.x = 5, p3.y = c。
这个例子的目的是展示一些泛型通过 impl 声明而另一些通过方法定义声明的情况。这里泛型参数 X1 和Y1 声明于 impl 之后,因为他们与结构体定义相对应。而泛型参数 X2 和 Y2 声明于 fn mixup 之后,因为他们只是相对于方法本身的。