Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

rust 泛型

本章内容,初学只需要掌握基本泛型语法和泛型函数即可,示例代码和泛型结构体以及泛型trait等对应章节学完再学。

1. 泛型编程是什么?目的是什么?

泛型编程是一种编程范式,让你在编写代码时不指定具体的数据类型,而是使用“类型占位符”。当你实际使用这段代码时,再填入具体的类型(如 i32f64String 等)。

1.1 为什么需要泛型?

假设你要编写一个函数,用来交换两个整数的值:

#![allow(unused)]
fn main() {
fn swap_i32(a: i32, b: i32) -> (i32, i32) {
    (b, a)
}
}

如果还要交换两个浮点数,你可能再写一个函数:

#![allow(unused)]
fn main() {
fn swap_f64(a: f64, b: f64) -> (f64, f64) {
    (b, a)
}
}

这样会带来大量重复代码,而且每个新类型都需要重新实现。泛型正是为了解决这类问题而生的——你只需写一个泛型函数,就可以处理所有类型:

#![allow(unused)]
fn main() {
fn swap<T>(a: T, b: T) -> (T, T) {
    (b, a)
}
}

其中 <T> 是一个类型占位符,调用时 Rust 会根据实际传入的参数自动推断出具体的 T(比如 i32f64String)。一份代码,任意类型,完美复用。

1.2 泛型的目的

  • 代码复用:一份代码可以服务于多种类型。
  • 类型安全:编译时仍然会进行严格的类型检查,避免运行时类型错误。
  • 提高抽象能力:让你专注于算法和结构,而不是具体类型。

2. 泛型的基本语法

Rust 中用尖括号 <T> 声明泛型参数,T 是习惯使用的名称(可以任意命名,但通常用大写字母)。下面分别介绍泛型在函数、结构体以及 trait 中的用法。

2.1 泛型函数

基本语法结构

fn 函数名<类型参数>(参数名: 类型参数) -> 返回类型 {
    // 函数体
}

调用示例片段(仅展示定义及调用方式)

#![allow(unused)]
fn main() {
fn identity<T>(value: T) -> T {
    value
}

// 调用示例(类型自动推断)
let a = identity(42);      // T 为 i32
let b = identity(3.14);    // T 为 f64
}

2.2 泛型结构体

基本语法结构

struct 结构体名<类型参数> {
    字段名: 类型参数,
    // 其它字段...
}

调用示例片段

#![allow(unused)]
fn main() {
struct Box<T> {
    item: T,
}

impl<T> Box<T> {
    fn new(item: T) -> Self {
        Box { item }
    }
  
    fn get(&self) -> &T {
        &self.item
    }
}

// 创建不同类型的实例
let int_box = Box::new(100);        // Box<i32>
let str_box = Box::new(String::from("hello")); // Box<String>
}

2.3 泛型 trait

基本语法结构

trait Trait名<类型参数> {
    // 方法签名中可以使用该类型参数
    fn 方法名(&self, 参数: 类型参数);
}

与结构体不同,trait 的泛型参数表示该 trait 是“泛型化的”,实现时可以为同一个类型多次实现该 trait(针对不同的具体类型)。

调用示例片段

#![allow(unused)]
fn main() {
trait ToString<T> {
    fn convert(&self, value: T) -> String;
}

// 为 i32 实现 ToString<f64>
impl ToString<f64> for i32 {
    fn convert(&self, value: f64) -> String {
        format!("整数 {} 和 浮点数 {}", self, value)
    }
}

// 调用
let num = 42;
let result = num.convert(3.14);   // result 为 String 类型
}

3. 综合示例:一个泛型容器(带讲解)

下面我们实现一个更完整的例子:一个可以存储两个相同类型值的对偶容器 Pair,并演示其在不同类型下的完整使用。

// 泛型结构体:存储两个相同类型的值
struct Pair<T> {
    first: T,
    second: T,
}

impl<T> Pair<T> {
    // 构造函数
    fn new(first: T, second: T) -> Self {
        Pair { first, second }
    }

    // 获取第一个值的引用
    fn first(&self) -> &T {
        &self.first
    }

    // 获取第二个值的引用
    fn second(&self) -> &T {
        &self.second
    }

    // 交换两个值的位置
    fn swap(&mut self) {
        std::mem::swap(&mut self.first, &mut self.second);
    }

    // 消费 self,返回两个值的元组
    fn into_tuple(self) -> (T, T) {
        (self.first, self.second)
    }
}

// 一个泛型函数:取出 Pair 的第一个字段
fn take_first<T>(pair: Pair<T>) -> T {
    pair.first
}

fn main() {
    // 使用整数类型
    let mut int_pair = Pair::new(10, 20);
    println!("({}, {})", int_pair.first(), int_pair.second());
    int_pair.swap();
    println!("交换后: ({}, {})", int_pair.first(), int_pair.second());

    // 使用字符串类型
    let str_pair = Pair::new(String::from("Hello"), String::from("World"));
    let (s1, s2) = str_pair.into_tuple();
    println!("字符串对偶: ({}, {})", s1, s2);

    // 使用浮点类型 + 泛型函数
    let float_pair = Pair { first: 3.14, second: 2.718 };
    let first_val = take_first(float_pair);
    println!("取出的第一个浮点数: {}", first_val);
}