写过 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 更保险。

评论区

0 条评论

登录后可评论。

铁锈·Rust工具链 11 阅读