《Rust 代码瘦身营》:告别重复,拥抱“抽象之美”!


“欢迎来到《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
}

“cool!这两个函数长得一模一样,除了一个处理 i32,一个处理 char。”

这就像你为“穿蓝衣服的人”和“穿红衣服的人”分别写了一套完全相同的操作手册——太浪费了!


解法:提取“通用逻辑”——泛型登场!

Rust 说:

“别慌!我们有泛型(Generics)——
它就像一个‘万能模板’,可以代替任何具体类型!”

我们把上面两个函数合并成一个:

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![1, 2, 3];
let max_num = largest(&nums);  // T 自动变成 i32

let chars = vec!['a', 'b', 'c'];
let max_char = largest(&chars); // T 自动变成 char

成功瘦身!一个函数,搞定所有类型!

泛型还能用在哪儿?

不只函数,结构体、枚举也能用泛型!

1. 结构体泛型

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

let p1 = Point { x: 1, y: 2.5 };      // T=i32, U=f64
let p2 = Point { x: "hello", y: 'a' }; // T=&str, U=char

就像“万能收纳盒”:
你想放啥类型,它就变成啥类型!

2. 枚举泛型

还记得 Option<T>Result<T, E> 吗?

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

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

它们本身就是泛型的典范!
一个 Option,能装 i32StringFile……无所不能!

但泛型不是“万能胶”——它需要“行为保证”

回到 largest<T> 函数:

if item > largest { ... } // 编译错误!

问题来了:

“所有类型都能用 > 比较吗?”

比如,你能比较两个 String 吗?可以。
但你能比较两个 File 吗?不行!

所以 Rust 说:

“你得告诉我:T 必须支持‘比较’这个行为!”

这就引出了——


Trait:行为的“合同”

Trait 就像一份“能力合同”:

trait Comparable {
    fn compare(&self, other: &Self) -> bool;
}

但我们不用自己写,Rust 标准库有现成的:

use std::cmp::PartialOrd;

fn largest<T: PartialOrd>(list: &[T]) -> T {
    let mut largest = list[0];
    for &item in list {
        if item > largest {  // 现在可以了!
            largest = item;
        }
    }
    largest
}

T: PartialOrd 意思是:
T 必须实现了 PartialOrd trait,也就是支持比较操作!”

就像招聘:
“我们招程序员,但必须会写代码!”(会写代码 = trait)


终极挑战:生命周期(Lifetimes)——引用的“时间管理大师”

泛型不只是类型,还有时间

看这段代码:

fn longest(x: &str, y: &str) -> &str {
    if x.len() > y.len() { x } else { y }
}

看起来没问题?但 Rust 会报错:

“我怎么知道返回的引用,比 xy 活得更久?”

这就像问:

“你借了两本书,哪本借得更久?但你还没说你什么时候还!”

所以 Rust 引入了 生命周期参数

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

'a 是一个“生命周期变量”:
它保证 xy 和返回值都“活”得一样久。

就像约会:
“我们仨的见面时间,不能超过最短的那个能待的时间。”

总结:三大法宝,一网打尽

工具 作用 类比
泛型 <T> 抽象类型,避免重复代码 万能模板
Trait 约束泛型的行为 能力合同
生命周期 'a 管理引用的存活时间 时间管理大师

“恭喜各位!经过今天的《Rust 代码瘦身营》,你已经掌握了:
用泛型消灭重复代码
用 Trait 确保类型有正确行为
用生命周期防止悬垂引用”

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐