Rust 1.91.0重磅发布!ARM Windows正式跻身顶级支持,悬空指针警告上线,模式匹配顺


Rust 1.91.0正式发布,aarch64-pc-windows-msvc晋升Tier 1,新增悬空原始指针警告,并重构模式匹配绑定顺序以提升语义一致性与安全性。

本文内容基于Rust官方发布团队(The Rust Release Team)于2025年10月30日发布的公告。



一、Rust 1.91.0来了!程序员的“安全盔甲”又升级了

各位技术圈的朋友们注意啦!就在昨天,也就是2025年10月30日,Rust官方团队正式发布了Rust 1.91.0稳定版!如果你是用rustup管理工具链的老用户,只需在终端敲一行命令:rustup update stable,就能立刻升级到最新版本。如果你还没装过Rust,也别犹豫,赶紧去官网下载rustup,开启你的高效、安全编程之旅吧!

Rust这门语言,从诞生之初就立志让每一位开发者都能写出既可靠又高效的软件。它不用垃圾回收,却能杜绝内存泄漏;它没有运行时开销,却能防止数据竞争。而1.91.0版本,不仅延续了这一传统,还在平台支持、代码安全、语言语义等多个维度带来了重磅更新。



二、ARM架构Windows用户狂喜:aarch64-pc-windows-msvc正式晋升“Tier 1”顶级支持

先说个让ARM Windows用户拍手叫好的消息:Rust 1.91.0正式将aarch64-pc-windows-msvc目标平台提升为Tier 1支持级别!这意味着什么?简单来说,就是Rust团队现在对64位ARM架构的Windows系统给出了最高级别的承诺。

你可能不知道,Rust支持上百种编译目标,但并非每个平台都享受同等“待遇”。官方用三级体系来划分支持强度:Tier 3只是“能编译”,连测试都不跑;Tier 2能编译也有预编译二进制包,但不保证功能完全正常;而Tier 1则是“黄金标准”——每次代码合并,都会在该平台上跑完整测试套件,确保万无一失。

现在,搭载高通骁龙X Elite等ARM芯片的Windows笔记本、平板,终于可以像x86-64 Linux或macOS一样,获得Rust官方的全力保障。无论是开发本地应用、跨平台工具,还是构建系统级服务,你都可以放心使用Rust,再也不用担心“编译能过,运行就崩”的尴尬局面。这对推动ARM生态的开发者工具链建设,意义重大!

你可以通过以下命令验证是否支持该目标:

bash
rustc --print target-list | grep aarch64-pc-windows-msvc

或者直接交叉编译:

bash
cargo build --target aarch64-pc-windows-msvc


三、悬空原始指针?Rust现在会主动“提醒”你了!

接下来这个更新,堪称“安全守护神”的又一次进化。我们知道,Rust的借用检查器(borrow checker)能有效防止悬空引用(dangling references),但对原始指针(raw pointers)却一直“睁一只眼闭一只眼”——因为原始指针属于unsafe代码范畴,编译器默认你“知道自己在干什么”。

然而,现实是,很多开发者在写unsafe代码时,还是会不小心返回指向局部变量的原始指针。比如下面这段代码:

rust
fn f() -> *const u8 {
    let x = 0;
    &x
}

看起来好像没问题?但其实,函数返回后,局部变量x就会被销毁,返回的指针就变成了“悬空指针”。虽然这段代码本身不触发unsafe行为(因为没解引用),但一旦调用方在外部解引用,就会导致未定义行为,轻则程序崩溃,重则安全漏洞。

从1.91.0开始,Rust编译器会默认发出警告!你会看到类似这样的提示:


warning: a dangling pointer will be produced because the local variable <code>x</code> will be dropped
 --> src/lib.rs:3:5
  |
1 | fn f() -> *const u8 {
  |           --------- return type of the function is <code>*const u8</code>
2 |     let x = 0;
  |         - <code>x</code> is part the function and will be dropped at the end of the function
3 |     &x
  |     ^^
  |
  = note: pointers do not have a lifetime; after returning, the <code>u8</code> will be deallocated
    at the end of the function because nothing is referencing it as far as the type system is
    concerned
  = note: <code>#[warn(dangling_pointers_from_locals)]</code> on by default


这个新lint叫做dangling_pointers_from_locals,默认开启,级别为warn。它不会阻止你编译,但会像一位贴心的同事,在你踩坑前轻轻拉你一把。

如果你确实需要返回原始指针(比如在FFI接口中),可以通过显式标注关闭警告:

rust
#[allow(dangling_pointers_from_locals)]
fn f() -> *const u8 {
    let x = 0;
    &x
}

但请务必确保调用方不会在函数返回后解引用该指针,否则仍属未定义行为。未来,Rust团队还计划引入更多机制,帮助开发者更安全地编写unsafe代码——毕竟,真正的“零成本抽象”,不仅要高效,更要安全!



四、模式匹配的“顺序革命”:绑定写入与析构顺序终于合理了!

如果说前两个更新是“看得见的安全”,那接下来这个改动,就是Rust语言内部一次深层次的语义重构——它关乎模式匹配(pattern matching)中绑定(bindings)的写入顺序和析构顺序(drop order)。

你可能没意识到,但在复杂的match表达式中,尤其是使用|(or-patterns)时,变量绑定的创建和销毁顺序,其实会影响程序行为。比如:

rust
enum Message {
    A(String),
    B(String, i32),
}

fn process(msg: Message) {
    match msg {
        Message::A(x) | Message::B(x, _) => {
            println!("Received: {}", x);
        }
    }
    // x 在此处被析构
}

在这个or-pattern中,x可能来自A分支,也可能来自B分支。那么,当这个match块结束时,x应该按什么顺序被析构?更关键的是,在模式匹配过程中,x是在什么时候被“绑定”到值上的?

在1.91.0之前,Rust的处理逻辑存在一个微妙但重要的问题:绑定的析构顺序是基于最后一个子分支,而非第一个。这导致实际析构顺序与开发者直觉不符,甚至可能引发资源管理错误(比如文件句柄、锁等)。

为了解决这个问题(对应GitHub issue #142163),Rust团队在1.91.0中做了两项关键调整:

第一,匹配分支的析构顺序现在基于第一个子分支,而不是最后一个。这意味着,无论你写的是A(x) | B(x)还是B(x) | A(x),只要x在第一个出现的分支中被绑定,它的析构顺序就以此为准。

第二,模式绑定的降低(lowering)顺序现在严格按照代码书写顺序进行(当然,@绑定仍优先于左侧约束)。换句话说,在每个子分支内部,绑定的创建顺序,就等同于你把or-pattern内联展开后应有的顺序。

举个更具体的例子:

rust
struct Guard(&'static str);

impl Drop for Guard {
    fn drop(&mut self) {
        println!("Dropping {}", self.0);
    }
}

enum Data {
    Left(Guard, Guard),
    Right(Guard, Guard),
}

fn demo(data: Data) {
    match data {
        Data::Left(a, b) | Data::Right(b, a) => {
            println!("Inside match");
        }
    }
    println!("Match block exited");
}

在Rust 1.90及之前版本中,由于析构顺序基于最后一个子分支(即Right(b, a)),实际析构顺序可能是a先于b,即使Left(a, b)是第一个分支。但在1.91.0之后,析构顺序将严格遵循第一个子分支的结构,即a后于b析构(因为Left(a, b)a先绑定,b后绑定,而Rust的析构顺序是逆绑定顺序)。

你可以运行以下完整代码验证行为变化:

rust
// main.rs
struct Guard(&'static str);

impl Drop for Guard {
    fn drop(&mut self) {
        eprintln!("Dropping {}", self.0);
    }
}

enum Test {
    X(Guard, Guard),
    Y(Guard, Guard),
}

fn main() {
    let t = Test::X(Guard("A"), Guard("B"));
    match t {
        Test::X(a, b) | Test::Y(b, a) => {
            eprintln!("In match block");
        }
    }
    eprintln!("End of main");
}

在1.91.0之前,输出可能是:


In match block
Dropping A
Dropping B
End of main

而在1.91.0之后,输出将变为:


In match block
Dropping B
Dropping A
End of main

因为现在以Test::X(a, b)为基准,a先绑定,b后绑定,析构时b先于a

这项改动虽然看似微小,但影响深远。它让模式匹配的行为更加可预测、可推理,尤其在涉及自定义Drop类型或资源管理时,能避免隐蔽的逻辑错误。官方也明确指出:这是一个破坏性变更(breaking change),需要经过“火山口运行”(crater run)全面测试,并获得语言团队正式批准。这也体现了Rust团队对语言稳定性和正确性的极致追求。



五、为什么这个“顺序”问题如此重要?

你可能会问:不就是变量销毁的顺序变了一下吗?至于大动干戈?

答案是:在系统编程中,顺序就是契约

想象一下,你正在实现一个数据库连接池,其中每个连接都持有一个锁和一个网络句柄。你在match中解构连接对象:

rust
match conn {
    Active(lock, socket) | Idle(lock, socket) => {
        // 处理连接
    }
}

如果socket先于lock被析构,而socket的Drop实现中需要获取锁(比如发送关闭信号),就会导致死锁或panic。过去,这种顺序可能因编译器内部实现细节而“随机”变化,开发者很难察觉。

现在,Rust 1.91.0通过统一以“主绑定”(即首次出现的绑定)为基准,确保了析构顺序的确定性。这不仅是语言设计的优雅胜利,更是对开发者心智负担的切实减轻。

此外,这次重构还为未来更复杂的模式匹配优化铺平了道路。比如,团队提到一个更大胆的设想:在运行时跟踪具体匹配的是哪个or分支,从而动态决定绑定顺序。虽然这次没上,但基础已经打好。



六、如何测试新特性?参与Beta和Nightly,成为Rust的“先锋用户”

Rust每六周发布一个稳定版,背后是庞大的测试和验证体系。如果你希望提前体验新功能,甚至帮助官方发现bug,强烈建议你切换到Beta或Nightly通道:

- 切换到Beta版:rustup default beta
- 切换到Nightly版:rustup default nightly

Beta版是下一个稳定版的候选,通常非常稳定;Nightly则包含最新实验性功能,适合尝鲜和深度测试。无论你发现什么问题,都可以通过Rust的GitHub仓库提交issue——你的每一次反馈,都在推动这门语言变得更好。



七、Rust的哲学:安全、效率与开发者体验的三重奏

回顾Rust 1.91.0的三大核心更新:ARM Windows的顶级支持,体现了对平台生态的重视;悬空指针警告,强化了内存安全的边界;模式匹配顺序重构,则优化了语言语义的一致性。这三点,恰恰呼应了Rust的三大支柱:可靠性、高效性与开发者体验。

Rust从不追求“语法糖”的堆砌,而是致力于在底层机制上做到滴水不漏。它相信,真正的生产力,来自于减少bug、减少不确定性、减少心智负担。而1.91.0,正是这一理念的又一次生动实践。



八、结语:拥抱变化,编写更安全的未来

技术世界日新月异,但Rust始终如一:它是一门为长期工程而生的语言。1.91.0或许没有引入炫目的新语法,但它在看不见的地方,默默加固了系统的地基。对于每一位使用Rust的开发者而言,这意味着更少的崩溃、更少的漏洞、更少的深夜调试。

所以,别再犹豫了!升级到Rust 1.91.0,让你的代码在ARM Windows上飞驰,让悬空指针无处遁形,让模式匹配如你所愿。未来已来,只待你执码前行。