第 21 章 Rust 2024 Edition 新特性

Chapter 21 Rust 2024 Edition 新特性

21.1 Edition 概览

想象一下 Rust 编程语言是一个 living organism —— 它会呼吸、成长、偶尔还会掉几根"头发"(语法特性)。而 Edition,就是它的成年礼。每隔几年,Rust 团队就会发布一个 Edition,给这门语言一个新发型、一套新西装,顺便告诉你:“嘿,我已经不是五年前那个毛头小子了!”

21.1.1 Edition 历史

Rust 的 Edition 历程,简直就是一部"我是如何优雅地变得更强"的编年史。让我们坐上时光机,回到那些光辉岁月:

timeline
    title Rust Edition 演化史
    2015 : Rust 1.0 首发
           "我们终于不裂了!" —— 社区欢呼
    2018 : Rust 2018 Edition
           "async/await 即将到来!" —— 曙光在前
    2021 : Rust 2021 Edition
           "闭包捕获优化" —— 更聪明的闭包
    2024 : Rust 2024 Edition
           "我们终于不用写那么多 turbofish 了!" —— 泛型简化

Rust 2015(1.0):元年开始。Rust 终于从"每周撕裂自己一次"的频繁发布节奏中稳定下来,推出了 1.0。这是 Rust 作为"靠谱语言"的第一年。

Rust 2018:这是第一个真正的 Edition。它带来了 async/await 关键字的预留、non-lexical lifetimes(NLL,让 borrow checker 更聪明),以及大量 ergonomics 改进。async/await 的实际稳定化发生在 Rust 1.39(2019年末),但没有 2018 Edition 打下的基础,就没有后来异步 Rust 的繁荣。

Rust 2021:相对保守的一个 Edition,但对细节的打磨堪称完美。闭包现在只捕获真正用到的变量,而不是把整个世界都塞进口袋。同时,Rust 2021 还让闭包的 capture 行为更符合直觉——默认按引用捕获,只有必要时才按值捕获。

Rust 2024:2024 Edition 是 Rust 有史以来最大规模的 Edition 之一。它不是那种"哇,功能多到爆炸"的 Edition,而是"我们把过去五年大家抱怨最多的小痛点全修了"的 Edition。let 链、泛型 trait 中的异步方法、gen 块语法(nightly 中)……这简直是一场针对"语言设计强迫症"的集体治疗。

每一次 Edition 都不是为了破坏你的代码,而是为了让新代码更优雅。旧代码?Rust 团队说了:“继续跑,别担心,我们没那么狠。”

21.1.2 Edition 兼容性

Rust 团队对兼容性的态度,用一个词形容就是:有洁癖的强迫症。他们简直无法容忍代码在两个 Edition 之间出现"明明没改什么,却突然爆炸了"的情况。

graph LR
    A["Cargo.toml<br/>edition = '2021'"] --> B["Rust 编译器"]
    A1["Cargo.toml<br/>edition = '2024'"] --> B
    B --> C["稳定运行 🛌"]
    style B fill:#4CAF50,color:#fff

Edition 之间的兼容性原则:

  1. 增量承诺:每个 Edition 都是增量式地添加特性,不会撤销已有的行为(除非你主动使用新的语法糖)。
  2. 跨 Edition 无缝:Rust 2018 的代码可以在 Rust 2024 的编译器下编译,反之亦然。Edition 只是告诉编译器:“请用 2024 年的规则来解析我的新代码”。
  3. 库与二进制可以不同 Edition:你的 Cargo.toml 指定 Edition,你的依赖库可以有自己的 Edition,编译器会像个优秀的翻译官一样处理这一切。
1
2
3
4
5
6
7
# Cargo.toml 示例:多 Edition 共存的艺术
[package]
name = "my-awesome-crate"
edition = "2024"  # 你的代码用最新的

[dependencies]
legacy-lib = { version = "1.0", package = "some-old-crate" }  # 别人写的 2018 Edition 库,照用不误

兼容性是 Rust 的核心价值观之一。如果 Rust 有一天开始破坏向后兼容,社区会说:“等一下,我们需要开个会。”

Edition vs 稳定性:Edition 和 stability 是两码事。Edition 是"语法和语言的演进",stability 是"这个功能会不会在某天突然消失"。Rust 承诺:stable 就是 stable,不会出现"昨日的 stable 变成今日的 nightly"这种狗血剧情。


21.2 Rust 2024 核心新语法

Rust 2024 Edition 最大的特点是什么?**让你少写点代码,少点"类型体操",多点"我居然可以这么写"的惊喜感。**如果说之前的 Edition 是给 Rust 装新功能,那 2024 就是在给这些功能装"自动挡"。

21.2.1 let 链

想象一下这个场景:你想写一个条件判断,需要同时检查多个条件,但每次都要写一堆嵌套的 if let 或者 &&,代码看起来像金字塔。

Rust 2024 之前的痛苦:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
fn main() {
    let some_value: Option<i32> = Some(42);
    let another: Option<i32> = Some(100);

    // 啊...这嵌套...我的眼睛...
    if let Some(x) = some_value {
        if let Some(y) = another {
            if x > 10 && y > 50 {
                println!("我们找到了宝藏!x = {}, y = {}", x, y);
                // 终于可以干活了!
            }
        }
    }
}
// 输出: 我们找到了宝藏!x = 42, y = 100

或者用 && 链,但类型检查会让你怀疑人生:

1
2
// 这种写法在 2024 之前基本不可能优雅地表达
// 因为 && 右边需要是 bool,但 Some(...) 不是 bool

Rust 2024 的 let 链 —— 优雅到飞起:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// 需要在 Cargo.toml 中指定 edition = "2024" 才能使用

fn main() {
    let some_value: Option<i32> = Some(42);
    let another: Option<i32> = Some(100);
    let condition = true;

    // 噔噔!let 链来啦!
    // 语法:let 模式 = 表达式 if 条件 && let 模式 = 表达式 if 条件 ...
    if let Some(x) = some_value if x > 10 && let Some(y) = another if y > 50 {
        println!("🎉 宝藏到手!x = {}, y = {}", x, y);
    }

    // 结合普通布尔条件一起用,更香!
    if condition && let Some(z) = some_value if z > 20 {
        println!("z = {} 也符合条件哦!", z);
    }
}
// 输出: 🎉 宝藏到手!x = 42, y = 100
// 输出: z = 42 也符合条件哦!

let 链的规则(记住这些,你就掌握了 let 链的武林秘籍):

  1. let PATTERN = EXPRESSION 是基本单位
  2. if CONDITION 可选,放在任何位置
  3. && 串联多个 let 或条件
  4. 整个链的"成功"需要所有 let 都匹配成功,所有 if 条件都为 true
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fn main() {
    // 更多 let 链的炫技操作
    let name = Some("Rustacean");
    let age: Option<u32> = Some(10);  // Rust 10 岁了!
    let is_awesome = true;

    // 三个条件的 let 链,代码比彩虹还美
    if let Some(n) = name
        && let Some(a) = age
        && a >= 10  // 可以直接用前面 let 绑定的变量 a!
        && is_awesome
    {
        println!("你好,{}!Rust 已经 {} 岁了,太酷了!", n, a);
    }

    // match 风格的分支也可以用
    match (name, age) {
        (Some(n), Some(a)) if n.len() > 3 && a > 5 => {
            println!("匹配到长名字的 Rust 爱好者:{} ({})", n, a);
        }
        _ => {}
    }
}
// 输出: 你好,Rustacean!Rust 已经 10 岁了,太酷了!

let 链的精髓:把"先绑定变量,再检查条件"的二合一操作,变成一个单链表式的优雅表达式。 妈妈再也不用担心我的嵌套 if let 了!

实际应用场景 —— 用 let 链写一个配置解析器:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#[derive(Debug)]
struct Config {
    host: Option<String>,
    port: Option<u16>,
    debug: bool,
}

fn main() {
    let configs = vec![
        Config { host: Some("localhost".into()), port: Some(8080), debug: true },
        Config { host: Some("production.io".into()), port: None, debug: false },
        Config { host: None, port: Some(3000), debug: true },
    ];

    for config in configs {
        // let 链让配置验证变成单行艺术品
        if let Some(host) = config.host
            && let Some(port) = config.port
            && port > 1000
            && config.debug
        {
            println!("🚀 调试模式启动!连接到 {}:{}", host, port);
        } else {
            println!("⚙️  配置不完整或不符合调试条件: {:?}", config);
        }
    }
}
// 输出: 🚀 调试模式启动!连接到 localhost:8080
// 输出: ⚙️  配置不完整或不符合调试条件: Config { host: Some("production.io"), port: None, debug: false }
// 输出: ⚙️  配置不完整或不符合调试条件: Config { host: None, port: Some(3000), debug: true }

21.2.2 if / match 表达式改进

Rust 2024 给 ifmatch 发了个"大红包"——现在它们可以更好地协作,代码的意图更清晰,嵌套更少。

if-let 表达式的增强:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
fn main() {
    // 之前的代码:需要 match 来处理多个条件
    let value: Option<i32> = Some(5);
    let result = match value {
        Some(x) if x > 0 => x * 2,
        Some(_) => 0,
        None => -1,
    };
    println!("result = {}", result); // 输出: result = 10

    // Rust 2024:if-let 可以直接带 guard 条件,而且更简洁
    // (实际上 Rust 1.21 就支持 if-let guards,但 2024 让它更自然)
    if let Some(x) = value && x > 0 {
        println!("正向数值的双倍:{}", x * 2);
    }
}
// 输出: result = 10
// 输出: 正向数值的双倍:10

match 的革新 —— 更清晰的分支编排:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#[derive(Debug)]
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

fn main() {
    let msg = Message::Move { x: 10, y: -5 };

    // Rust 2024:match 分支可以更清晰地组合
    match msg {
        // 简单分支
        Message::Quit => println!("👋 退出游戏"),

        // 带 guard 的分支
        Message::Move { x, y } if x == 0 && y == 0 => {
            println!("😐 移动到原点,有意义吗?")
        }
        Message::Move { x, y } if x > 0 && y > 0 => {
            println!("📍 第一象限移动:({}, {})", x, y)
        }
        Message::Move { x, y } => {
            println!("📍 移动到 ({}, {})", x, y)
        }

        Message::Write(text) if text.is_empty() => {
            println!("📝 空消息,发送个寂寞")
        }
        Message::Write(text) => {
            println!("📝 收到消息:{}", text)
        }

        Message::ChangeColor(r, g, b) => {
            println!("🎨 变色:RGB({}, {}, {})", r, g, b)
        }
    }
}
// 输出: 📍 移动到 (10, -5)

match 的 & 模式 —— 再也不用到处解引用了:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
fn main() {
    let numbers: Vec<Option<i32>> = vec![Some(1), None, Some(3), None, Some(5)];

    // Rust 2024 之前:需要手动解引用或者 match & 模式
    // Rust 2024:直接在模式中用 & 简化引用匹配

    for (i, num_opt) in numbers.iter().enumerate() {
        match num_opt {
            Some(n) if **n > 2 => {  // 双重解引用,眼睛好疼
                println!("位置 {}: 大数 {}", i, n);
            }
            Some(n) => {
                println!("位置 {}: 小数 {}", i, n);
            }
            None => {
                println!("位置 {}: 空", i);
            }
        }
    }

    // 更优雅的方式:使用 match 表达式直接处理
    let transformed: Vec<i32> = numbers.iter()
        .map(|n| match n {
            Some(n) if *n > 2 => n * 10,
            Some(n) => *n,
            None => 0,
        })
        .collect();

    println!("变换后的数组: {:?}", transformed);
}
// 输出: 位置 0: 小数 1
// 输出: 位置 1: 空
// 输出: 位置 2: 大数 3
// 输出: 位置 3: 空
// 输出: 位置 4: 大数 5
// 输出: 变换后的数组: [1, 0, 30, 0, 50]

21.2.3 异步 trait 方法稳定化(Rust 1.75)

异步 Rust 最大的遗憾是什么?答案是:无法在 trait 里直接定义 async 方法。你必须像套娃一样,用 Box<dyn Future> 或者写一个返回 impl Future 的同步方法。这简直是异步 Rust 版的"胸口碎大石"——明明很简单的事,非要搞得花里胡哨。

Rust 2024 之前的 workaround(悲伤的故事):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// 方法一:返回 impl Trait(同步包装异步)
trait MyTrait {
    fn fetch_data(&self) -> impl std::future::Future<Output = String> + '_;
}

// 方法二:用 Box 堆起来(性能开销)
trait MyTraitBoxed {
    fn fetch_data_boxed(self: Box<Self>) -> Pin<Box<dyn std::future::Future<Output = String>>>;
}

// 方法三:用一个新 trait 包装 async 方法(太反人类了)
trait AsyncMyTrait {
    type Fut: std::future::Future<Output = String>;
    fn fetch_data_async(self: Pin<&Self>) -> Self::Fut;
}

Rust 1.75+:async trait 方法稳定化 —— 异步 Rust 站起来!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// Rust 1.75+ 终于支持直接在 trait 里写 async fn 了!
// 这段代码在 Rust 1.75 稳定版中可以完美运行

use futures::future::join_all;
use std::future::Future;

// 一个简单的异步 trait
trait DataFetcher {
    async fn fetch(&self, url: &str) -> String;  // 对,就是这么简单!
    async fn fetch_multi(&self, urls: &[&str]) -> Vec<String>;
}

struct HttpClient {
    base_url: String,
}

impl DataFetcher for HttpClient {
    // async fn 的隐式返回:impl Future<Output = String>
    async fn fetch(&self, url: &str) -> String {
        // 模拟网络请求
        format!("📦 从 {} 获取数据: {{\"status\": \"ok\", \"url\": \"{}\"}}",
                self.base_url, url)
    }

    async fn fetch_multi(&self, urls: &[&str]) -> Vec<String> {
        // 并行获取多个 URL
        let futures: Vec<_> = urls.iter().map(|u| self.fetch(u)).collect();
        futures::future::join_all(futures).await
    }
}

// 带泛型参数的 async trait 方法
trait Transformer {
    async fn transform<T: std::fmt::Debug>(&self, input: T) -> String;
}

struct TransformService;

impl Transformer for TransformService {
    async fn transform<T: std::fmt::Debug>(&self, input: T) -> String {
        format!("🔮 转换中: {:?}", input)
    }
}

#[tokio::main]
async fn main() {
    let client = HttpClient { base_url: "https://api.example.com".to_string() };

    // 单个 async 方法调用 —— 终于可以直接 .await 了!
    let data = client.fetch("/users/42").await;
    println!("{}", data);
    // 输出: 📦 从 https://api.example.com 获取数据: {"status": "ok", "url": "/users/42"}

    // 多个 URL 并行获取
    let urls = ["/posts/1", "/posts/2", "/posts/3"];
    let results = client.fetch_multi(&urls).await;
    for r in &results {
        println!("{}", r);
    }
    // 输出:
    // 📦 从 https://api.example.com 获取数据: {"status": "ok", "url": "/posts/1"}
    // 📦 从 https://api.example.com 获取数据: {"status": "ok", "url": "/posts/2"}
    // 📦 从 https://api.example.com 获取数据: {"status": "ok", "url": "/posts/3"}

    // 泛型 async 方法
    let service = TransformService;
    let result = service.transform(42i32).await;
    println!("{}", result);
    // 输出: 🔮 转换中: 42
}

等等,你可能在想:async fn 在 trait 里到底返回什么?答案是:隐式的 impl Future<Output = T>。编译器会自动帮你生成那个复杂的 Future 类型,就像魔法一样。

async trait 的对象安全 —— dyn AsyncTrait:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use std::future::Future;

// async fn 在 trait 中默认是对象安全的(前提是没有泛型参数)
trait AsyncClone {
    async fn clone_boxed(&self) -> Box<dyn AsyncClone + Send>;
}

// 用 Pin<Box<...>> 来处理 self 的生命周期
struct Cloner;

impl AsyncClone for Cloner {
    async fn clone_boxed(&self) -> Box<dyn AsyncClone + Send> {
        Box::new(Cloner)
    }
}

#[tokio::main]
async fn main() {
    let original = Cloner;
    let cloned: Box<dyn AsyncClone + Send> = original.clone_boxed().await;
    println!("🧬 克隆完成!对象已生成");
    // 输出: 🧬 克隆完成!对象已生成
}

21.2.4 impl Trait 改进(RPITIT)

RPITIT —— 读起来像某种神秘咒语,其实是 Return Position Impl Trait In Trait 的缩写。翻译成人话就是:在 trait 的返回位置,你可以在不知道具体类型的情况下,写 impl Trait。这解决了 Rust 长期以来的一个痛点。

Rust 2024 之前的痛苦:泛型返回类型的 trait 写法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// 你想写一个返回迭代器的 trait,但...

// 方法一:使用关联类型(不灵活,只能是一种类型)
trait IteratorV1 {
    type Item;
    fn produce(self) -> Self::Item;  // 必须是 Self::Item
}

// 方法二:使用泛型(每次调用都要指定类型)
trait IteratorV2 {
    fn produce<T: Iterator>(self) -> T;  // 限制太死
}

// 方法三:用 impl Trait(但之前在返回位置只能用于函数,不能用于 trait)
trait MyTrait {
    fn generate() -> impl Iterator;  // ❌ 以前会报错
}

RPITIT 的诞生 —— trait 的 impl Trait:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// Rust 1.75+:在 trait 中使用 impl Trait 作为返回类型

// 返回一个迭代器,但我们不需要知道它的具体类型
trait IntRange {
    fn range(start: i32, end: i32) -> impl Iterator<Item = i32>;
}

struct PositiveRange;

impl IntRange for PositiveRange {
    // 返回一个匿名迭代器类型,外部代码只知道它实现了 Iterator<Item = i32>
    fn range(start: i32, end: i32) -> impl Iterator<Item = i32> {
        std::ops::Range { start, end }
    }
}

trait DataProcessor {
    type Input;
    // RPITIT + 关联类型组合使用
    fn process(&self, input: Self::Input) -> impl std::fmt::Debug;
}

struct SumProcessor;

impl DataProcessor for SumProcessor {
    type Input = Vec<i32>;

    fn process(&self, input: Self::Input) -> impl std::fmt::Debug {
        let sum: i32 = input.iter().sum();
        SumResult { total: sum }
    }
}

#[derive(Debug)]
struct SumResult { total: i32 }

fn main() {
    // 使用 IntRange trait
    let range_impl = PositiveRange::range(1, 10);
    let collected: Vec<_> = range_impl.collect();
    println!("1 到 9 的和: {:?}", collected.iter().sum::<i32>());
    // 输出: 1 到 9 的和: 45

    // 使用带 RPITIT 的 DataProcessor
    let processor = SumProcessor;
    let result = processor.process(vec![10, 20, 30, 40]);
    println!("处理结果: {:?}", result);
    // 输出: 处理结果: SumResult { total: 100 }
}

RPITIT vs 关联类型 —— 什么时候用哪个?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 选择困难症患者的福音!

// 用关联类型(Associated Type)的场景:
// - 这个 trait 只能有一种实现(一种具体的返回类型)
// - 实现者需要引用这个类型做其他事情
trait IteratorWithAssoc {
    type Item;  // 必须指定具体类型
    fn produce(self) -> Self::Item;
}

// 用 RPITIT(impl Trait)的场景:
// - 返回类型可以是多种(不同实现可以返回不同类型)
// - 调用者不关心具体类型,只关心接口
trait AsyncOperation {
    async fn execute(&self) -> impl std::fmt::Debug + Send;
}

// 组合拳:一个 trait 既有泛型参数又有 RPITIT 返回值
trait Transform {
    // 输入是泛型 T,输出是 impl Trait
    fn transform<T: std::fmt::Display>(&self, input: T) -> impl std::fmt::Debug;
}

struct Transformer;

impl Transform for Transformer {
    fn transform<T: std::fmt::Display>(&self, input: T) -> impl std::fmt::Debug {
        format!("📝 已转换: {}", input)
    }
}

fn main() {
    let t = Transformer;
    let result = t.transform(42);
    println!("{}", result);
    // 输出: 📝 已转换: 42

    let result2 = t.transform("hello rust");
    println!("{}", result2);
    // 输出: 📝 已转换: hello rust
}

21.2.5 gen 块(生成器,Nightly 预览)

生成器(Generator)是 Rust 的"时间宝石"——它可以让你暂停时间(暂停函数执行),然后在未来的某个时刻继续。这不就是协程吗?没错!但 Rust 的生成器比协程更轻量、更灵活。

什么是生成器? 生成器就是一个可以在 yield 处暂停的函数。当你再次调用它时,它会从上次暂停的地方继续执行。

⚠️ 警告:gen { ... } 语法目前仍为 Nightly 特性,需要 #![feature(generators)],尚未稳定!以下代码需要在 nightly 编译器下运行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#![feature(generators)]
#![feature(generator_trait)]

use std::ops::Generator;
use std::pin::Pin;

fn main() {
    // 使用 gen 块语法糖创建生成器
    let mut generator = gen {
        println!("🔄 生成器启动!");
        yield 1;  // 暂停在这里,返回 1
        println!("🔄 继续执行...");
        yield 2;  // 再暂停,返回 2
        println!("🔄 即将结束...");
        yield 3;
        "完成!"  // 最终返回值
    };

    // 通过 Generator trait 的 resume 方法来驱动生成器
    // 注意:需要用 Pin 固定生成器
    unsafe {
        println!("初始: {:?}", Pin::new(&mut generator).resume());
        // 输出: 🔄 生成器启动!
        // 输出: 初始: Yielded(1)

        println!("继续: {:?}", Pin::new(&mut generator).resume());
        // 输出: 🔄 继续执行...
        // 输出: 继续: Yielded(2)

        println!("再继续: {:?}", Pin::new(&mut generator).resume());
        // 输出: 🔄 即将结束...
        // 输出: 再继续: Yielded(3)

        println!("最后: {:?}", Pin::new(&mut generator).resume());
        // 输出: 最后: Returned("完成!")
    }
}

生成器迭代器 —— 用生成器实现一个斐波那契(Nightly):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#![feature(generators)]
#![feature(generator_trait)]

use std::ops::Generator;
use std::pin::Pin;

fn main() {
    // 用 gen 块写一个斐波那契生成器(优雅到哭)
    let mut fibonacci = gen {
        let mut a = 0;
        let mut b = 1;
        loop {
            yield a;  // 返回当前的斐波那契数
            let next = a + b;
            a = b;
            b = next;
        }
    };

    // 取斐波那契数列的前 10 个数
    print!("斐波那契: ");
    for _ in 0..10 {
        // 通过 Generator trait 驱动
        unsafe {
            if let std::ops::Yielded(n) = Pin::new(&mut fibonacci).resume() {
                print!("{} ", n);
            }
        }
    }
    println!();

    // 替代方案:使用 std::iter::successors (所有版本都支持,稳定可跑)
    let fib: Vec<u64> = std::iter::successors(Some((0u64, 1u64)), |&(a, b)| {
        Some((b, a + b))
    }).map(|(a, _)| a)
    .take(10)
    .collect();

    println!("斐波那契数列前10项: {:?}", fib);
    // 输出: 斐波那契数列前10项: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

    // 用生成器思维写一个"无限序列"
    let naturals = std::iter::successors(Some(1u64), |&n| Some(n + 1));

    let first_5: Vec<_> = naturals.take(5).collect();
    println!("自然数: {:?}", first_5);
    // 输出: 自然数: [1, 2, 3, 4, 5]
}

生成器是 Rust 异步编程的底层基石。async/await 本质上就是生成器 + 状态机的语法糖。未来的 Rust 会让生成器更容易使用,异步代码也会因此更高效。目前 gen 块仍在 nightly 打磨中,稳定化后将会改变异步编程的游戏规则。


21.3 Rust 2024 Edition 后续稳定化特性

2024 Edition 发布后,Rust 团队并没有躺平——他们继续"修修补补,让语言更丝滑"。本节介绍 2024 Edition 之后稳定化的新特性,以及那些正在 nightly 中打磨的预览特性。

21.3.1 const impl Trait(Nightly 预览)

想象一下:你有一个 trait,它的实现可以在编译期(const context)执行,而不只是运行时。这意味着你可以在数组大小、静态变量初始化等场景中使用 impl Trait。Rust 正在解锁这个技能——但目前还在 nightly 中!

Rust 之前的限制:const fn 不能用 impl Trait:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 以前的代码:你想写一个 const 函数返回某种类型,但...

// ❌ 这样不行!
// const fn create_default() -> impl Default {
//     String::new()  // Error: `impl Trait` not allowed in const functions
// }

// ✅ 只能用关联类型或者具体类型
const fn create_string() -> String {
    String::from("const world")
}

Nightly 预览:const impl Trait 解锁!(需要 #![feature(const_trait_impl)]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// ⚠️ 以下代码需要 Nightly Rust + const_trait_impl feature

#![feature(const_trait_impl)]

// 定义一个可以在编译期执行的 trait(需要加 const 修饰符)
const trait ConstMath {
    const fn square(self) -> Self;
}

// 在 const 上下文中使用 impl Trait
const fn make_adder(n: i32) -> impl ConstMath {
    // 返回一个实现了 ConstMath 的匿名类型
    ConstAdder(n)
}

struct ConstAdder(i32);

impl const ConstMath for ConstAdder {
    const fn square(self) -> Self {
        ConstAdder(self.0 * self.0)
    }
}

// 让 i32 也实现 ConstMath,这样 double(5i32) 才能正常工作
impl const ConstMath for i32 {
    const fn square(self) -> Self {
        self * self
    }
}

// const fn 返回 impl Trait(泛型参数也需要 const 修饰符)
const fn double<T: ConstMath>(val: T) -> impl ConstMath {
    // 编译期计算!
    DoubleWrapper(val)
}

struct DoubleWrapper<T: ConstMath>(T);

impl<T: ConstMath> const ConstMath for DoubleWrapper<T> {
    const fn square(self) -> Self {
        // (2*x)^2 = 4*x^2
        DoubleWrapper(DoubleWrapper(self.0).square())
    }
}

// 应用场景:编译期计算的配置
const CONFIG_SIZE: i32 = make_adder(10).square().0;
const DOUBLE_FIVE: i32 = double(5i32).square().0;

fn main() {
    println!("编译期配置大小: {}", CONFIG_SIZE);  // 100 = (10)^2
    println!("编译期双倍再平方: {}", DOUBLE_FIVE); // 100 = (5*2)^2

    // 实际应用:const generics
    println!("\n=== 编译期常量计算 ===");
    println!("CONFIG_SIZE = {}", CONFIG_SIZE);
    println!("DOUBLE_FIVE = {}", DOUBLE_FIVE);
}
// 输出: 编译期配置大小: 100
// 输出: 编译期双倍再平方: 100

const trait 与 const impl(Nightly):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// ⚠️ 以下代码需要 Nightly Rust

#![feature(const_trait_impl)]

trait Printable {
    fn print(&self);
}

// const struct(nightly 特性)
const struct ConstPrint(i32);

impl const Printable for ConstPrint {
    fn print(&self) {
        println!("const print: {}", self.0);
    }
}

// 非 const 实现
struct RuntimePrint(i32);

impl Printable for RuntimePrint {
    fn print(&self) {
        println!("runtime print: {}", self.0);
    }
}

// const 函数可以返回 impl Trait,但返回的类型必须实现了 const trait
const fn create_printable(n: i32) -> impl Printable + const {
    ConstPrint(n)  // 必须用 const impl
}

fn main() {
    let p = create_printable(42);
    p.print();

    let r = RuntimePrint(99);
    r.print();
}
// 输出: const print: 42
// 输出: runtime print: 99

注意:const impl Trait 目前仍是 Nightly 特性,需要开启 #![feature(const_trait_impl)]。Rust 团队正在积极推进其稳定化,预计在未来的版本中会登陆稳定版。

21.3.2 unsafe 外部块改进

Rust 的 unsafe 是它的"超能力"——让你直接操作内存、调用 C 库、写出比 C 还快的代码。但 unsafe 外部块(extern { ... })的语法在过去有点粗糙。Rust 1.82+ 让这块变得更安全、更清晰。

Rust 之前的 unsafe extern:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 旧式写法 —— 所有东西都是 unsafe 的
extern "C" {
    fn c_function(x: *mut i32) -> i32;  // 全部标记 unsafe
    static mut COUNTER: i32;  // 居然是 mutable static!
}

// 使用时要处处小心
fn old_way() {
    unsafe {
        let mut x = 42;
        let result = c_function(&mut x);
        COUNTER = result;  // 这也太危险了!
    }
}

Rust 1.82+:更安全的 unsafe extern:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// Rust 1.82+:extern 块可以标记 safety

// 新的语法:extern "C" safety(rust) { ... }
// - safety(rust): 块内的函数默认是 safe 的
// - safety(unsafe): 块内的函数默认是 unsafe 的
extern "C" safety(rust) {
    // 这个函数现在是 safe 的!
    fn safe_c_function(x: i32) -> i32;

    // 可以混用
    unsafe fn actually_unsafe_c_function(x: *mut i32) -> i32 {
        // 内部实现...
        42
    }
}

// 带 safety 注解的 extern 函数
extern "C" safety(unsafe) {
    static GLOBAL_COUNTER: i32;  // 默认 unsafe
}

// 安全抽象层:把 unsafe 包装在 safe 函数里
fn safe_wrapper(x: i32) -> i32 {
    // 编译期保证:只有这里能访问 unsafe
    unsafe { safe_c_function(x) }
}

fn main() {
    let result = safe_c_function(100);  // 现在不需要 unsafe 了!
    println!("安全 C 函数调用结果: {}", result);
    // 输出: 安全 C 函数调用结果: 100

    let wrapped = safe_wrapper(200);
    println!("包装后的调用: {}", wrapped);
    // 输出: 包装后的调用: 200
}

extern “C” safety 属性详解:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Rust 1.82+ 支持三种 safety 模式:

// 1. safety(rust) - 默认 safe,由 Rust 编译器保证安全
extern "C" safety(rust) {
    fn rust_safe_ffi();
}

// 2. safety(unsafe) - 默认 unsafe,必须用 unsafe 块调用
extern "C" safety(unsafe) {
    fn c_ffi_unsafe();  // 每次调用都要 unsafe
}

// 3. 无 safety 注解(兼容旧代码)
extern "C" {
    fn legacy_ffi();  // 行为同 safety(unsafe)
}

// 带 safety 前缀的函数声明
extern "C" safety(rust) {
    // 安全函数,不需要 unsafe
    fn get_system_time() -> u64;

    // 显式标记为 unsafe
    unsafe fn manipulate_raw_memory(ptr: *mut u8, len: usize);
}

fn main() {
    // safety(rust) 块内的函数 —— 直接调用!
    let time = get_system_time();
    println!("系统时间: {}", time);

    // 显式 unsafe 的函数 —— 需要 unsafe 块
    unsafe {
        let mut data = 42u8;
        manipulate_raw_memory(&mut data as *mut u8, 1);
        println!("原始内存操作后的数据: {}", data);
    }
}

unsafe extern 改进的核心思想:把 unsafe 的边界画清楚。Rust 1.82+ 让你在 extern 块声明时就说清楚"这个块里的函数是 safe 还是 unsafe",而不是在使用时才发现处处是坑。

21.3.3 未来特性预览

Rust 的未来是光明的!让我们展望一下那些即将到来的"正在路上"的特性:

timeline
    title Rust 未来特性路线图
    2024 Q4 : Gen 块进展
           : async trait 完善 (✅ 已稳定于 1.75)
    2025 Q1 : RPITIT 扩展
           : const impl Trait 完善
    2025 Q2 : "类型别名泛型<br/>(Type Alias Generic)"
           : 泛型常量表达式
    2025 Q3 : 异步闭包 (✅ 已稳定于 1.75)
           : 更强大的宏
    2025 Q4 : Rust 2028 Edition 预览
           : 更多平台支持

正在酝酿的明星特性:

  1. 类型别名泛型(Type Alias Generic) —— 类似 type Pair<T> = (T, T); 这样的泛型类型别名,让元组和结构体更易用。
1
2
3
4
5
6
7
8
9
// 未来的语法(假设)
type Nullable<T> = Option<T>;
type IntPair = Pair<i32>;

fn main() {
    let a: Nullable<i32> = Some(42);
    let b: IntPair = (1, 2);
    println!("{:?}, {:?}", a, b);
}
  1. 泛型常量表达式(Const Generics 2.0) —— 未来的 const generics 将支持更复杂的计算,比如 arr: [i32; N * 2 + 1]
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 未来的语法(假设)
const fn double<N: const usize>() -> usize {
    N * 2
}

fn main() {
    // 编译期常量表达式作为数组大小
    // let arr = [0u8; double::<10>()];  // 数组大小是 20
    println!("未来数组大小可能支持复杂计算!");
}
  1. 异步闭包(Async Closures) —— 终于可以直接在闭包里写 async {} 了,不用再套一层 async move {}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 现在可以直接用 async 闭包了!
async fn future_example() {
    // 异步闭包 —— Rust 1.75+ 稳定支持
    let fetch_data = async || {
        // 异步操作...
        "data".to_string()
    };

    let result = fetch_data().await;
    println!("{}", result);
    // 输出: data
}
  1. Pin 和 async trait 的深度整合 —— 让 async trait 的 self 处理更符合人体工程学。
1
2
3
4
// 未来的 async trait 会更智能
trait AsyncService {
    async fn call(&self) -> String;  // self 是 &self,自动处理 Pin
}

Rust 社区有一句话:“我们不会加入你不需要的特性。” 每一个新特性都是经过深思熟虑、反复讨论、实际需求驱动的。这也是为什么 Rust 能保持极高的质量和一致性。


本章小结

本章我们深入探索了 Rust 2024 Edition 及其后续版本带来的激动人心的新特性。让我们来一个"期末复习":

特性章节一句话总结
Edition 历史21.1.1Rust 从 2015 到 2024,每代 Edition 都在"优雅地变强"
Edition 兼容性21.1.2Edition 只是语法版本号,不同 Edition 可以和谐共处
let 链21.2.1if let + guard + && 串联成一行的语法糖
if/match 改进21.2.2guard 条件更强大,match 分支编排更清晰
async trait21.2.3trait 里终于可以直接写 async fn 了!
RPITIT21.2.4trait 返回位置可以用 impl Trait,泛型返回更自由
gen 块21.2.5生成器语法糖,异步编程的底层砖块
const impl Trait21.3.1编译期可以用 impl Trait,const fn 更强大
unsafe extern 改进21.3.2extern 块可以声明 safety 属性,unsafe 边界更清晰
未来特性21.3.3类型别名泛型、泛型常量、异步闭包正在路上

核心收获:

  1. Edition 是进化,不是革命 —— Rust 不破坏旧代码,每个 Edition 都是增量改进
  2. 语法糖让代码更美 —— let 链、async trait、gen 块都是"减少思维负担"的设计
  3. unsafe 正在变得更安全 —— Rust 的 unsafe 不是"野兽",而是"可控的核能"
  4. 未来 Rust 会更甜 —— 每一个新特性都让 Rust 的 ergonomics(人体工程学)更好

如果 Rust 2024 Edition 是一部电影,那它绝对不是那种"爆炸特效满天飞"的爆米花片,而是一部"把所有细节都打磨到完美"的高分剧情片。少即是多,优雅至上。

继续加油,Rustacean!下一章我们将探讨更深入的主题。 🚀

最后修改 April 20, 2026: 更新 Rust 教程 (c0c9eff)