首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >13-Rust 教程 - 泛型

13-Rust 教程 - 泛型

作者头像
LarryLan
发布2026-04-28 12:24:25
发布2026-04-28 12:24:25
680
举报

泛型

一套代码走天下:泛型函数、结构体、枚举全解析

🎬 引入

你有没有写过这种代码:一个函数处理 i32,另一个函数处理 f64,再写一个处理 String……然后发现这三个函数的逻辑一模一样,只是类型不同!

我当时就这么干过,写了五个几乎一样的函数,只是类型不同。后来同事问我:"你是复制粘贴狂魔吗?"

泛型就是来拯救你的!它让你用一套代码处理多种类型,就像洗衣机上的"万能模式"——不管洗什么衣服,都能搞定(虽然可能没有专用模式洗得那么好,但够用)。

Rust 的泛型特别强大,而且是在编译时完成的,没有运行时开销。这意味着你用了泛型,性能和手写专用代码一样快!编译器帮你干了所有脏活累活。

📌 核心概念

什么是泛型?

泛型就是"类型参数化"。普通函数是这样的:

代码语言:javascript
复制
fn add_i32(a: i32, b: i32) -> i32 {
    a + b
}

泛型函数是这样的:

代码语言:javascript
复制
fn add<T>(a: T, b: T) -> T {
    a + b  // ❌ 等等,这不能编译!后面说为什么
}

<T> 就是类型参数,可以是任何类型。T 是"Type"的缩写,你也可以用别的名字,但大家都用 T,你也别搞特殊。

泛型的好处

  1. 减少重复代码 - 写一次,到处用
  2. 类型安全 - 编译时检查,不会运行时出错
  3. 零成本抽象 - 性能和手写专用代码一样
  4. 代码更清晰 - 意图明确,易于维护

泛型的应用场景

  • 函数参数和返回值
  • 结构体字段
  • 枚举变体
  • 方法实现
  • Trait 定义

💻 代码示例

泛型函数

代码语言:javascript
复制
// 最简单的泛型函数
fn identity<T>(x: T) -> T {
    x
}

fn main() {
    let a = identity();           // T 推断为 i32
    let b = identity("hello");     // T 推断为 &str
    let c = identity::<f64>(3.14); // 显式指定 T
}

多个类型参数

代码语言:javascript
复制
fn pair<T, U>(a: T, b: U) -> (T, U) {
    (a, b)
}

fn main() {
    let p = pair(, "hello");      // (i32, &str)
    let q = pair(3.14, true);      // (f64, bool)
}

泛型结构体

代码语言:javascript
复制
// 单个泛型参数
struct Point<T> {
    x: T,
    y: T,
}

// 多个泛型参数
struct Point2<T, U> {
    x: T,
    y: U,
}

fn main() {
    let p1 = Point { x: , y:  };           // Point<i32>
    let p2 = Point { x: 1.0, y: 4.0 };        // Point<f64>
    let p3 = Point2 { x: , y: 10.0 };        // Point2<i32, f64>
}

泛型枚举

代码语言:javascript
复制
// Option 和 Result 就是泛型枚举
enum MyOption<T> {
    Some(T),
    None,
}

enum MyResult<T, E> {
    Ok(T),
    Err(E),
}

fn main() {
    let a: MyOption<i32> = MyOption::Some();
    let b: MyOption<&str> = MyOption::None;
    
    let c: MyResult<i32, &str> = MyResult::Ok();
    let d: MyResult<i32, &str> = MyResult::Err("出错了");
}

泛型方法实现

代码语言:javascript
复制
struct Point<T> {
    x: T,
    y: T,
}

impl<T> Point<T> {
    fn new(x: T, y: T) -> Self {
        Point { x, y }
    }
    
    fn x(&self) -> &T {
        &self.x
    }
    
    fn y(&self) -> &T {
        &self.y
    }
}

// 只为特定类型实现方法
impl Point<f32> {
    fn distance_from_origin(&self) -> f32 {
        (self.x.powi() + self.y.powi()).sqrt()
    }
}

fn main() {
    let p = Point::new(, );
    println!("x = {}", p.x());
    
    let p2 = Point::new(3.0, 4.0);
    println!("距离原点:{}", p2.distance_from_origin());
}

泛型 + Trait 约束

代码语言:javascript
复制
use std::fmt::Display;

// T 必须实现 Display trait
fn print_item<T: Display>(item: T) {
    println!("{}", item);
}

// 多个约束
fn print_and_debug<T: Display + std::fmt::Debug>(item: T) {
    println!("{}", item);
    println!("{:?}", item);
}

// where 子句(复杂约束时用)
fn complex_function<T, U>(t: T, u: U) 
where
    T: Display + Clone,
    U: Clone + std::fmt::Debug,
{
    println!("{}", t);
    let _t2 = t.clone();
    println!("{:?}", u);
    let _u2 = u.clone();
}

fn main() {
    print_item();              // ✅ i32 实现 Display
    print_item("hello");        // ✅ &str 实现 Display
    // print_item(vec![1,2]);   // ❌ Vec 没实现 Display
}

🐛 错误示例 - 这些坑你别踩

错误 1:泛型函数里瞎操作

代码语言:javascript
复制
fn add<T>(a: T, b: T) -> T {
    a + b  // ❌ 编译错误!
}

编译器说:

代码语言:javascript
复制
binary operation `+` cannot be applied to type `T`

解释: 不是所有类型都能相加!你得告诉编译器 T 必须支持加法。

正确做法:

代码语言:javascript
复制
use std::ops::Add;

fn add<T>(a: T, b: T) -> T 
where
    T: Add<Output = T> 
{
    a + b
}

错误 2:泛型结构体方法不匹配

代码语言:javascript
复制
struct Point<T> {
    x: T,
    y: T,
}

impl Point<f32> {
    fn new(x: i32, y: i32) -> Self {  // ❌ 类型不匹配!
        Point { x, y }
    }
}

编译器说:

代码语言:javascript
复制
mismatched types: expected f32, found i32

正确做法:

代码语言:javascript
复制
impl Point<f32> {
    fn new(x: f32, y: f32) -> Self {
        Point { x, y }
    }
}

错误 3:忘记加 Trait 约束

代码语言:javascript
复制
fn print_all<T>(items: &[T]) {
    for item in items {
        println!("{}", item);  // ❌ 编译错误!
    }
}

编译器说:

代码语言:javascript
复制
the trait `Display` is not implemented for `T`

正确做法:

代码语言:javascript
复制
use std::fmt::Display;

fn print_all<T: Display>(items: &[T]) {
    for item in items {
        println!("{}", item);
    }
}

🐛 常见坑点

1. 泛型不是万能的

泛型函数里的类型 T 不能随便操作,必须通过 Trait 约束告诉编译器 T 支持什么操作。

代码语言:javascript
复制
// ❌ 这样不行
fn double<T>(x: T) -> T {
    x * 
}

// ✅ 这样才行
fn double<T>(x: T) -> T 
where
    T: std::ops::Mul<Output = T> + From<u8>
{
    x * T::from(2u8)
}

2. 泛型代码膨胀

每个具体类型都会生成一份代码,类型太多会增加编译时间和二进制大小。

代码语言:javascript
复制
// 这会生成多份代码
let a = identity();      // i32 版本
let b = identity(1.0);    // f64 版本
let c = identity("hi");   // &str 版本

解决方法:用 Trait 对象(后面会讲)。

3. 生命周期 + 泛型

代码语言:javascript
复制
struct Ref<T> {
    value: &T,  // ❌ 编译错误!缺少生命周期
}

// 正确写法
struct Ref<'a, T> {
    value: &'a T,
}

引用类型必须加生命周期参数!

🎯 实战案例

案例 1:通用栈数据结构

代码语言:javascript
复制
struct Stack<T> {
    items: Vec<T>,
}

impl<T> Stack<T> {
    fn new() -> Self {
        Stack { items: Vec::new() }
    }
    
    fn push(&mut self, item: T) {
        self.items.push(item);
    }
    
    fn pop(&mut self) -> Option<T> {
        self.items.pop()
    }
    
    fn peek(&self) -> Option<&T> {
        self.items.last()
    }
    
    fn is_empty(&self) -> bool {
        self.items.is_empty()
    }
    
    fn len(&self) -> usize {
        self.items.len()
    }
}

fn main() {
    // 整数栈
    let mut int_stack = Stack::new();
    int_stack.push();
    int_stack.push();
    int_stack.push();
    println!("{:?}", int_stack.pop());  // Some(3)
    
    // 字符串栈
    let mut str_stack = Stack::new();
    str_stack.push("hello");
    str_stack.push("world");
    println!("{:?}", str_stack.pop());  // Some("world")
}

案例 2:泛型链表

代码语言:javascript
复制
struct Node<T> {
    value: T,
    next: Option<Box<Node<T>>>,
}

impl<T> Node<T> {
    fn new(value: T) -> Self {
        Node { value, next: None }
    }
    
    fn with_next(value: T, next: Node<T>) -> Self {
        Node { 
            value, 
            next: Some(Box::new(next)) 
        }
    }
}

// 简单的泛型链表
struct LinkedList<T> {
    head: Option<Box<Node<T>>>,
}

impl<T> LinkedList<T> {
    fn new() -> Self {
        LinkedList { head: None }
    }
    
    fn prepend(&mut self, value: T) {
        let new_node = Box::new(Node {
            value,
            next: self.head.take(),
        });
        self.head = Some(new_node);
    }
    
    fn pop(&mut self) -> Option<T> {
        self.head.take().map(|node| {
            self.head = node.next;
            node.value
        })
    }
}

fn main() {
    let mut list = LinkedList::new();
    list.prepend();
    list.prepend();
    list.prepend();
    
    println!("{:?}", list.pop());  // Some(1)
    println!("{:?}", list.pop());  // Some(2)
}

案例 3:泛型缓存

代码语言:javascript
复制
use std::collections::HashMap;
use std::hash::Hash;

struct Cache<K, V> {
    data: HashMap<K, V>,
    capacity: usize,
}

impl<K: Eq + Hash + Clone, V: Clone> Cache<K, V> {
    fn new(capacity: usize) -> Self {
        Cache {
            data: HashMap::new(),
            capacity,
        }
    }
    
    fn get(&self, key: &K) -> Option<V> {
        self.data.get(key).cloned()
    }
    
    fn put(&mut self, key: K, value: V) {
        // 简单实现:满了就清空
        if self.data.len() >= self.capacity && !self.data.contains_key(&key) {
            self.data.clear();
        }
        self.data.insert(key, value);
    }
    
    fn contains(&self, key: &K) -> bool {
        self.data.contains_key(key)
    }
    
    fn len(&self) -> usize {
        self.data.len()
    }
}

fn main() {
    let mut cache = Cache::new();
    cache.put("a", );
    cache.put("b", );
    cache.put("c", );
    
    println!("{:?}", cache.get(&"a"));  // Some(1)
    println!("{:?}", cache.get(&"d"));  // None
}

案例 4:泛型数学运算

代码语言:javascript
复制
use std::ops::{Add, Sub, Mul, Div};

struct Calculator<T> {
    value: T,
}

impl<T> Calculator<T> 
where
    T: Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + From<u8> + Copy,
{
    fn new(initial: T) -> Self {
        Calculator { value: initial }
    }
    
    fn add(&mut self, other: T) {
        self.value = self.value + other;
    }
    
    fn sub(&mut self, other: T) {
        self.value = self.value - other;
    }
    
    fn mul(&mut self, other: T) {
        self.value = self.value * other;
    }
    
    fn div(&mut self, other: T) {
        self.value = self.value / other;
    }
    
    fn get(&self) -> T {
        self.value
    }
}

fn main() {
    let mut calc = Calculator::new(10.0);
    calc.add(5.0);
    calc.mul(2.0);
    calc.sub(5.0);
    calc.div(3.0);
    println!("结果:{}", calc.get());  // 10.0
}

🧠 思维导图

13-泛型 (Generics)
13-泛型 (Generics)

📝 小结

  1. 泛型是类型参数化:一套代码处理多种类型,减少重复
  2. 泛型结构体和枚举Vec<T>Option<T>Result<T, E> 都是泛型
  3. Trait 约束:告诉编译器泛型类型支持什么操作
  4. 单态化:编译时为每个具体类型生成代码,零运行时开销
  5. where 子句:复杂约束时用,代码更清晰

下篇预告: 泛型虽然强大,但每次都要写一堆 Trait 约束,烦不烦?Trait 就是来拯救你的!下篇我们讲讲 Trait 到底是什么,为什么它是 Rust 最强大的特性之一!

🔗 参考资料

  • Rust Book - Generics
  • Rust By Example - Generics
  • std::vec::Vec
  • std::option::Option
  • std::result::Result
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-04-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Larry的Hub 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 泛型
    • 🎬 引入
    • 📌 核心概念
      • 什么是泛型?
      • 泛型的好处
      • 泛型的应用场景
    • 💻 代码示例
      • 泛型函数
      • 多个类型参数
      • 泛型结构体
      • 泛型枚举
      • 泛型方法实现
      • 泛型 + Trait 约束
      • 🐛 错误示例 - 这些坑你别踩
    • 🐛 常见坑点
      • 1. 泛型不是万能的
      • 2. 泛型代码膨胀
      • 3. 生命周期 + 泛型
    • 🎯 实战案例
      • 案例 1:通用栈数据结构
      • 案例 2:泛型链表
      • 案例 3:泛型缓存
      • 案例 4:泛型数学运算
    • 🧠 思维导图
    • 📝 小结
    • 🔗 参考资料
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档