2026年1月22日,Rust 1.93 正式发布。你可能好奇,距上一版本仅一个多月,这个版本能带来什么新东西?
虽然它不是一个颠覆性的大版本,但其中包含了一系列“补漏”性质的改进,专为解决那些在实际开发中让人“用着不得劲”的痛点。从静态链接的稳定性到汇编代码的简洁性,再到内存操作的高级控制,每个改动都相当实在。
升级方式一如既往地简单:
rustup update stable
接下来,我们来详细看看这些值得关注的更新。
musl 1.2.5:静态链接体验升级
如果你曾尝试构建静态链接的 Rust 二进制文件,那么对 musl 一定不陌生。musl 是一个轻量级的 C 标准库实现,常用于创建不依赖系统动态库的可执行文件。这对于希望生成小巧、可移植的容器镜像(例如基于 Alpine Linux)的场景尤为重要。
Rust 1.93 将所有 *-linux-musl 目标平台统一升级到 musl 1.2.5。此次升级修复了之前版本中存在的多个问题,特别是与 DNS 解析相关的一些顽疾,使得静态链接更加可靠。
潜在问题与解决方案
musl 1.2.4 移除了部分陈旧的 libc 符号。如果你的项目依赖一个较旧版本的 libc crate,在编译时可能会遇到 “undefined reference” 错误。解决方法通常是更新依赖:
cargo update
如果你想验证静态构建是否正常,可以执行以下步骤:
# 添加 musl 目标
rustup target add x86_64-unknown-linux-musl
# 进行静态构建
RUSTFLAGS="-C target-feature=+crt-static" cargo build --target x86_64-unknown-linux-musl --release
如果遇到类似 undefined reference to 'open64' 的错误,将 libc crate 升级到 0.2.146 或更高版本即可解决。
汇编中的 #[cfg]:告别代码复制粘贴
在进行底层开发,如编写与 CPU 特性相关的内联汇编时,经常需要根据不同的编译条件生成不同的指令。过去,你不得不复制整段 asm! 宏代码块。
例如,以前需要这样写:
// 支持 SSE2 的版本
#[cfg(target_feature = "sse2")]
unsafe {
std::arch::asm!(
"pxor %xmm0, %xmm0",
options(nostack, preserves_flags)
);
}
// 不支持 SSE2 的版本
#[cfg(not(target_feature = "sse2"))]
unsafe {
std::arch::asm!(
"mov $0, %eax",
options(nostack, preserves_flags)
);
}
现在,你可以直接在 asm! 宏内部使用 #[cfg] 属性进行条件编译,极大地提升了代码的简洁性和可维护性:
use std::arch::asm;
unsafe {
asm!(
"nop",
#[cfg(target_feature = "sse2")]
"pxor %xmm0, %xmm0",
options(nostack, preserves_flags)
);
}
这样,“pxor %xmm0, %xmm0” 这行指令只有在开启 SSE2 特性时才会被包含。这对于操作系统、加密算法等需要精细控制指令集的领域来说,是一个解放生产力的改进。
全局分配器与 TLS:解除多年限制
在 Rust 1.93 之前,如果你在自定义的全局分配器的 alloc 或 dealloc 方法中尝试使用 thread_local! 或获取当前线程信息,编译器会因潜在的“重入死循环”风险而阻止你。这限制了许多合理的分配器设计,比如按线程统计内存使用量。
Rust 1.93 解除了这个限制。标准库现在内部提供了保障机制,防止在分配器内部访问线程本地存储(TLS)时发生死循环。现在,你可以这样实现一个简单的线程感知分配器:
use std::alloc::{GlobalAlloc, Layout, System};
use std::cell::Cell;
struct MyAlloc;
thread_local! {
static ALLOCATED: Cell<usize> = Cell::new(0);
}
unsafe impl GlobalAlloc for MyAlloc {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let p = System.alloc(layout);
if !p.is_null() {
// 现在可以安全地使用 thread_local! 了
ALLOCATED.with(|c| c.set(c.get() + layout.size()));
}
p
}
// dealloc 方法类似...
}
#[global_allocator]
static A: MyAlloc = MyAlloc;
这个改进为更复杂的内存管理和性能剖析工具铺平了道路。
新稳定的实用 API
除了上述核心改进,Rust 1.93 还稳定了一批让日常编码更顺手的 API。
数组与切片便捷转换
处理网络协议或二进制数据时,经常需要在切片和固定大小数组间转换。新方法 slice::as_array 和 slice::as_array_mut 让操作更流畅:
let buf: &[u8] = &[1, 2, 3, 4, 5];
let arr = buf.get(..4).and_then(|s| s.as_array()); // 返回 Option<&[u8; 4]>
零拷贝内存操作:into_raw_parts
String::into_raw_parts 和 Vec::into_raw_parts 的稳定化是进行 零拷贝 和 FFI (外部函数接口) 交互的利器。它们允许你将 String 或 Vec 解构成原始指针、长度和容量,从而完全接管其内存,之后还能安全地重建。

let s = String::from("hello");
let (ptr, len, cap) = s.into_raw_parts();
// ... 将 ptr, len 传递给 C 函数,或进行其他底层操作 ...
// 从原始部件安全重建
let s2 = unsafe { String::from_raw_parts(ptr, len, cap) };
assert_eq!(s2, "hello");
注意:调用 into_raw_parts 后,你必须负责最终释放这块内存(通常通过 from_raw_parts 重建后再丢弃,或手动调用 dealloc),否则会导致内存泄漏。
VecDeque 的条件弹出
VecDeque::pop_front_if 和 pop_back_if 方法简化了基于条件消费队列元素的操作:
// 以前
if q.front().map(|&x| x % 2 == 1).unwrap_or(false) {
q.pop_front();
}
// 现在
q.pop_front_if(|&x| x % 2 == 1);
这在实现定时任务队列(TTL过期)或特定规则的消费者时非常有用。
高精度 Duration 构造
Duration::from_nanos_u128 方法允许你直接从 u128 类型的纳秒数创建 Duration 对象,避免了手动计算秒和纳秒的麻烦:
use std::time::Duration;
let d = Duration::from_nanos_u128(10_u128.pow(12)); // 1,000,000,000,000 纳秒,即 1000 秒
如果提供的纳秒数超过了 Duration 能表示的最大值,该方法会 panic,这比静默溢出产生一个错误的时间值更安全。
升级建议
升级到 Rust 1.93 的步骤很简单:
- 更新工具链:
rustup update stable
- 更新项目依赖:
cargo update
如果你使用 musl 目标进行静态链接,建议按前文方法进行一次验证性构建,确保一切正常。
总结
总的来说,Rust 1.93 版本着眼于打磨细节和移除障碍:
- musl 1.2.5 提升了静态链接的可靠性。
- 汇编中的
#[cfg] 减少了底层代码的冗余。
- 全局分配器与 TLS 的兼容 为高级内存管理打开了大门。
- 一系列 API 的稳定(尤其是零拷贝操作)让系统级编程和性能优化更加得心应手。
这些变化或许不会引起轰动,但它们共同标志着 Rust 生态正在走向更精细、更成熟的阶段,让开发者能更专注于解决实际问题,而非与工具链搏斗。如果你对 Rust 的系统级能力或高性能编程感兴趣,不妨在云栈社区的 Rust 板块与其他开发者交流更多实战经验。