写过 Rust 的人都踩过这个坑——Range 存进 struct 就要 clone,今天 core::range::Range 把这件事彻底原生化了
写解析器、编译器、语法高亮器的时候,经常要记录一段源码的起止位置。Span { start: usize, end: usize },见过吧?Range 用不了,因为 core::ops::Range 没有实现 Copy。存个范围值就要拆成两个字段,或者每次用都 clone 一下,这个坑 Rust 踩了十几年。
Rust 1.96(2026-05-28)把这个坑彻底填上了。
核心就一句话:新 Range 可以 Copy 了。
核心变化:core::range
RFC 3550 落地。Rust 1.96 新增了三个类型:
- core::range::Range
- core::range::RangeFrom
- core::range::RangeInclusive
这三个类型实现了 IntoIterator 而不是直接实现 Iterator。Iterator 有内部可变状态,如果同时 Copy,复制出来的迭代器会产生重复或跳过元素——所以 Rust 不让它们同时实现 Copy。
新方案把「范围」和「迭代器」拆开了:范围本身是纯数据,可以 Copy;用的时候转成迭代器就行。
三行代码感受一下:
use core::range::Range;
#[derive(Copy, Clone)]
struct Span(Range<usize>);
fn slice(span: Span, src: &str) -> &str {
&src[span.0] // 不用 clone,不用拆字段
}
以前只能这么写:
#[derive(Copy, Clone)]
struct Span {
start: usize,
end: usize,
}
fn slice(span: Span, src: &str) -> &str {
&src[span.start..span.end] // 每次用都要 ..span.end
}
assert_matches! 也稳定了
Rust 1.96 还顺手稳定了 assert_matches! 和 debug_assert_matches! 两个宏。以前要写 assert!(matches!(val, pat)),失败信息只有一句「assertion failed」。现在:
use std::assert_matches::assert_matches;
assert_matches!(response.status, 200..=299);
// 失败时打印: assert `left matches right` failed
// left: 404
这个宏没有放进 prelude,因为第三方 crates 已经有同名宏,怕冲突。用的时候手动 import:
use core::assert_matches::assert_matches;
WebAssembly 链接行为变了
Rust 1.96 对 Wasm 目标默认关闭了 –allow-undefined 标志。以前链接时遇到未定义符号会自动从 env 模块导入,现在直接报链接错误。这个改动是为了更早暴露配置问题,比如符号名拼错或者漏了链接某个库。
如果你真的需要显式导入外部符号,可以:
// 方案 1:RUSTFLAGS
RUSTFLAGS="-Clink-arg=--allow-undefined" cargo build --target wasm32-unknown-unknown
// 方案 2:源码标注
#[link(wasm_import_module = "env")]
extern {}
迁移注意事项
range 语法 0..1 仍然产生旧版 Range 类型(core::ops::Range)。真正的字面量迁移要在未来的 Edition 里才会发生。
现在建议:公共 API 用 impl RangeBounds,兼容新旧两种 Range:
pub fn process(range: impl RangeBounds<usize>) {}
个人项目可以直接用 core::range::Range,早用早享受。
升级命令:
rustup update stable
rustc --version # 应该显示 rustc 1.96.0
两个 Cargo 安全修复
1.96 还修了两个 Cargo CVE:CVE-2026-5223(中危,第三方 registry 的 tarball 处理 symlink 可能覆盖其他 crate 缓存)和 CVE-2026-5222(低危,sparse protocol URL 标准化问题)。crates.io 用户不受影响,用第三方 registry 的升级。
下一步
如果你的项目里有 (usize, usize) 或者 (u32, u32) 拆分存储的范围值,现在可以检查一下能不能换成 Range 了。Parser、lexer、语法高亮这类场景受益最直接。
迁移前记得跑一遍测试,特别是涉及 Copy 语义的边界情况。新旧 Range 类型行为基本兼容,但如果你有 impl Range 的泛型约束,替换成 impl RangeBounds 更保险。
评论区
登录后可评论。