趣味学RUST基础篇(泛型扩展)

发布于:2025-09-04 ⋅ 阅读:(29) ⋅ 点赞:(0)

Rust 泛型从“复制粘贴”到“万能神器”

上一节我们认识了泛型,这一节我们要深入它的世界,看看它如何在函数、结构体、枚举和方法中大显身手!”

第一关:函数泛型——消灭“孪生函数”

还记得这两个长得一模一样的函数吗?

fn largest_i32(list: &[i32]) -> &i32 { ... }
fn largest_char(list: &[char]) -> &char { ... }

它们就像一对“双胞胎”,除了处理的类型不同,其他完全一样!

Rust 说:“别写两个!用一个‘万能函数’搞定!”

泛型登场!

我们给函数加个“类型占位符”——T

fn largest<T>(list: &[T]) -> &T {
    let mut largest = &list[0];
    for item in list {
        if item > largest {  // 等等!这里有问题!
            largest = item;
        }
    }
    largest
}

T 是“某种类型”,具体是谁?调用时再定!

调用它:

let nums = vec![34, 50, 25, 100, 65];
let max = largest(&nums);  // T = i32

let chars = vec!['y', 'm', 'a', 'q'];
let max = largest(&chars); // T = char

一个函数,通吃所有类型!

但……编译报错:

error[E0369]: 不能对 &T 使用 `>` 操作!

问题来了:不是所有类型都能比较大小啊!你能比较两个 File 谁更大吗?不能!

解法:加个“能力认证”——Trait 约束!

我们告诉 Rust:

T 必须会‘比较大小’这个技能!”

use std::cmp::PartialOrd;

fn largest<T: PartialOrd>(list: &[T]) -> &T {
    // ...
}

T: PartialOrd 意思是:“T 必须实现了 PartialOrd trait(能力证书)!” 现在编译通过!安全又灵活!

第二关:结构体泛型——打造“万能容器”

场景:点坐标 Point

我们想表示一个点,但坐标可能是整数、小数、字符串……甚至 emoji!

方案一:所有坐标同类型
struct Point<T> {
    x: T,
    y: T,
}

let p1 = Point { x: 5, y: 10 };      // T = i32
let p2 = Point { x: 1.0, y: 4.0 };   // T = f64

漂亮!但……

let p3 = Point { x: 5, y: 4.0 }; //  报错!x 和 y 类型不同!

不行!T 只能代表一种类型。

方案二:允许不同类型!

用两个泛型参数:

struct Point<T, U> {
    x: T,
    y: U,
}

let p1 = Point { x: 5, y: 10 };      // T=i32, U=i32
let p2 = Point { x: 1.0, y: 'a' };   // T=f64, U=char
let p3 = Point { x: "Hi", y: vec![1,2,3] }; // T=&str, U=Vec<i32>

自由!灵活!想放啥放啥! 小贴士:泛型太多?代码会变“天书”!一般别超过 3 个,否则该拆分模块了!

第三关:枚举泛型——“可选值”与“结果”的秘密

还记得 Option<T>Result<T, E> 吗?它们就是泛型的王者!

Option<T>:可能有值,也可能没有

enum Option<T> {
    Some(T),
    None,
}

T 是“可能存在的值的类型”
Some(5)Ti32
Some("hello")T&str
None → 没有值,但类型还是 Option<T>

Result<T, E>:成功或失败

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

T 是成功时的值类型(比如 File
E 是错误时的值类型(比如 io::Error

一个枚举,表达“两种可能”,类型完全自由!


第四关:方法泛型——给“万能结构体”加技能

我们可以在泛型结构体上定义方法!

示例 1:通用方法

impl<T> Point<T> {
    fn x(&self) -> &T {
        &self.x
    }
}

impl<T> 表示:这个实现也是泛型的!所有 Point<T> 实例都有 .x() 方法!

示例 2:只给特定类型加技能

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

这个方法只属于 Point<f32>
Point<i32> 没有这个方法,因为它不能开平方!

示例 3:方法自己也有泛型!

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,
        }
    }
}

这个方法更牛:它接受另一个 Point<V, W>
然后返回一个“混血”点:x 来自自己,y 来自别人!

let p1 = Point { x: 5, y: 10.4 };           // T=i32, U=f64
let p2 = Point { x: "Hello", y: 'c' };       // V=&str, W=char

let p3 = p1.mixup(p2);  // p3.x = 5 (i32), p3.y = 'c' (char)
                        // 类型:Point<i32, char>

跨类型“基因重组”!太酷了!


终极揭秘:泛型真的慢吗?——单态化魔法!

很多人担心:

“泛型是‘通用’的,是不是运行时要‘查类型’?会不会变慢?”

Rust 大笑:

“不存在的!我们用的是 单态化(Monomorphization) 魔法!”

魔法原理:

编译时,Rust 把泛型代码“展开”成多个具体版本

比如:

let integer = Some(5);     // T = i32
let float = Some(5.0);     // T = f64

编译器会生成:

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);

结果:运行时没有“泛型”!只有具体类型!
性能 ≈ 你手动写了两个函数!

就像“预制菜”:
你写的是“通用食谱”(泛型),
但上桌的是“定制大餐”(具体类型)!


总结:泛型四重奏

类型 语法 关键点
函数 fn func<T>(x: T) T: Trait 约束行为
结构体 struct S<T> { x: T } 多参数 T, U 更灵活
枚举 enum E<T> { V(T) } OptionResult 的秘密
方法 impl<T> S<T> { ... } 可通用,可特化,可自泛型

网站公告

今日签到

点亮在社区的每一天
去签到