Skip to content

泛型编程

泛型允许你编写适用于多种类型的代码,而不是为每种类型重复编写。泛型与 Trait 结合是 Rust 实现抽象的核心方式。

假设我们要找出列表中的最大值:

fn largest_i32(list: &[i32]) -> i32 {
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}
fn largest_char(list: &[char]) -> char {
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}

这两个函数几乎一样,只是类型不同。

fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let number_list = vec![34, 50, 25, 100, 65];
let result = largest(&number_list);
println!("最大的数字是 {}", result);
let char_list = vec!['y', 'm', 'a', 'q'];
let result = largest(&char_list);
println!("最大的字符是 {}", result);
}
  • <T>:声明泛型类型参数
  • T: PartialOrd + Copy:约束 T 必须实现这两个 trait
    • PartialOrd:可以比较大小
    • Copy:可以复制(用于 list[0]&item
fn largest<T: PartialOrd>(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
largest
}

不需要 Copy,因为我们返回的是引用。

struct Point<T> {
x: T,
y: T,
}
fn main() {
let integer = Point { x: 5, y: 10 };
let float = Point { x: 1.0, y: 4.0 };
}
struct Point<T, U> {
x: T,
y: U,
}
fn main() {
let both_integer = Point { x: 5, y: 10 };
let both_float = Point { x: 1.0, y: 4.0 };
let integer_and_float = Point { x: 5, y: 4.0 };
}

标准库中的 OptionResult

enum Option<T> {
Some(T),
None,
}
enum Result<T, E> {
Ok(T),
Err(E),
}
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
fn y(&self) -> &T {
&self.y
}
}
fn main() {
let p = Point { x: 5, y: 10 };
println!("p.x = {}", p.x());
}

注意 impl<T> 中的 <T> 声明。

impl Point<f64> {
fn distance_from_origin(&self) -> f64 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}

只有 Point<f64> 才有 distance_from_origin 方法。

struct Point<T, U> {
x: T,
y: U,
}
impl<T, U> Point<T, U> {
fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
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);
// 输出: p3.x = 5, p3.y = c
}
fn print_debug<T: std::fmt::Debug>(value: T) {
println!("{:?}", value);
}
use std::fmt::{Debug, Display};
fn print_both<T: Debug + Display>(value: T) {
println!("Debug: {:?}", value);
println!("Display: {}", value);
}

当约束复杂时,使用 where 更清晰:

use std::fmt::Debug;
fn some_function<T, U>(t: &T, u: &U) -> i32
where
T: Display + Clone,
U: Clone + Debug,
{
// ...
0
}
use std::fmt::Display;
struct Pair<T> {
x: T,
y: T,
}
// 所有 Pair<T> 都有 new 方法
impl<T> Pair<T> {
fn new(x: T, y: T) -> Self {
Self { x, y }
}
}
// 只有满足约束的 Pair<T> 才有 cmp_display 方法
impl<T: Display + PartialOrd> Pair<T> {
fn cmp_display(&self) {
if self.x >= self.y {
println!("最大的是 x = {}", self.x);
} else {
println!("最大的是 y = {}", self.y);
}
}
}

生命周期也是泛型的一种:

fn longest<'a, T>(x: &'a str, y: &'a str, ann: T) -> &'a str
where
T: Display,
{
println!("公告: {}", ann);
if x.len() > y.len() { x } else { y }
}
struct ImportantExcerpt<'a, T> {
part: &'a str,
metadata: T,
}
impl<'a, T> ImportantExcerpt<'a, T> {
fn level(&self) -> i32 {
3
}
}

Rust 在编译时对泛型代码进行单态化(monomorphization):为每个具体类型生成专门的代码。

let integer = Some(5);
let float = Some(5.0);

编译后相当于:

enum Option_i32 {
Some(i32),
None,
}
enum Option_f64 {
Some(f64),
None,
}
let integer = Option_i32::Some(5);
let float = Option_f64::Some(5.0);

这意味着:

  • 没有运行时开销
  • 泛型代码和手写的具体类型代码性能相同
  • 这就是 Rust 的”零成本抽象”
trait Factory {
type Output;
fn create(&self) -> Self::Output;
}
struct IntFactory;
impl Factory for IntFactory {
type Output = i32;
fn create(&self) -> Self::Output {
42
}
}
struct RequestBuilder<T> {
url: String,
body: Option<T>,
}
impl<T> RequestBuilder<T> {
fn new(url: &str) -> Self {
RequestBuilder {
url: url.to_string(),
body: None,
}
}
fn body(mut self, body: T) -> Self {
self.body = Some(body);
self
}
fn build(self) -> Request<T> {
Request {
url: self.url,
body: self.body,
}
}
}
struct Request<T> {
url: String,
body: Option<T>,
}
struct Locked;
struct Unlocked;
struct Door<State> {
_state: std::marker::PhantomData<State>,
}
impl Door<Locked> {
fn unlock(self) -> Door<Unlocked> {
println!("门已解锁");
Door { _state: std::marker::PhantomData }
}
}
impl Door<Unlocked> {
fn lock(self) -> Door<Locked> {
println!("门已上锁");
Door { _state: std::marker::PhantomData }
}
fn open(&self) {
println!("门打开了");
}
}
fn main() {
let door: Door<Locked> = Door { _state: std::marker::PhantomData };
// door.open(); // 编译错误!锁着的门不能打开
let door = door.unlock();
door.open(); // OK
}

编写泛型函数 largest,找出切片中的最大值:

fn largest<T: PartialOrd>(list: &[T]) -> &T {
// 你的代码
}
fn main() {
let numbers = vec![34, 50, 25, 100, 65];
println!("最大数字: {}", largest(&numbers));
let chars = vec!['y', 'm', 'a', 'q'];
println!("最大字符: {}", largest(&chars));
}

定义泛型结构体 Point<T>,实现方法获取 x 和 y:

struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
// 实现 x() 和 y() 方法
}
fn main() {
let p = Point { x: 5, y: 10 };
println!("x: {}, y: {}", p.x(), p.y());
}

Point<f64> 单独实现 distance_from_origin 方法:

impl Point<f64> {
fn distance_from_origin(&self) -> f64 {
// 计算到原点的距离
}
}
fn main() {
let p = Point { x: 3.0, y: 4.0 };
println!("距离: {}", p.distance_from_origin()); // 应输出: 5.0
}

编写函数,接收两个相同类型的值,打印较大的那个:

use std::fmt::Display;
fn print_larger<T>(a: T, b: T)
where
// 添加适当的约束
{
// 你的代码
}
fn main() {
print_larger(5, 10);
print_larger("apple", "banana");
}

掌握了泛型后,下一章我们将学习迭代器和闭包——Rust 函数式编程的核心。