Rust 1.96 的 Range 类型:把 Copy 从迭代器手里拿回来
Range 一直不是 Copy,因为它自己实现了 Iterator。Rust 1.96 稳定了一套新的 Range 类型:只实现 IntoIterator,于是可以 Copy。这篇讲这个区分为什么重要,以及它什么时候会改掉你熟悉的语法。
Rust 1.96.0(2026 年 5 月 28 日)稳定了一件看起来很小、但会慢慢渗透到很多代码里的事:新的 Range 类型。
背景是一句抱怨:Range 应该是 Copy,但它不是。
为什么不是
因为 Range 自己实现了 Iterator。而一个类型同时实现 Iterator 和 Copy 是个陷阱:Copy 意味着「赋值之后原来那份还能用」,于是 for x in r 之后 r 还在,但你刚刚把它迭代完了 —— 再迭代一次会得到什么?空?从头?语义立刻说不清。
所以标准库一直没给 Range 加 Copy,代价是它不能被方便地存起来复用。想在一个结构体里存「切片的一段」,只能拆成 start 和 end 两个字段:
struct Span {
start: usize,
end: usize,
}
这很蠢,但没办法。
RFC 3550 的解法:换成 IntoIterator
新稳定的是 core::range 下的三个类型:
core::range::Rangecore::range::RangeFromcore::range::RangeInclusive
关键区别只有一个:它们实现的是 IntoIterator,不是 Iterator。于是 Copy 不再是陷阱 —— 你复制一个 Range 不会复制「迭代到哪了」的状态,因为那个状态根本不在类型里。
于是上面那个结构体可以直接写:
use core::range::Range;
#[derive(Clone, Copy)]
pub struct Span(Range<usize>);
impl Span {
pub fn of(self, s: &str) -> &str {
&s[self.0]
}
}
顺带一个细节:新的 RangeInclusive 把字段公开了。老的那个不公开,是因为要隐藏「已经迭代完了」的内部状态 —— 新类型没有那个状态,所以不需要藏。
语法还没换
现在写 0..1 得到的仍然是老的 core::ops 类型。官方说法是「未来的某个 edition 会改成 core::range 类型」。
也就是说:
- 现在换过去要显式写
core::range::Range { start, end }或转换 - 已有的
0..1代码行为不变,不会突然编译失败 - 但如果你现在写新代码、又想将来少改一次,可以直接用新类型
后续还会补 core::range::RangeFull 与 core::range::RangeTo(它们本来就不实现 Iterator、本来就已经是 Copy,所以只是重新导出),以及 core::range::legacy::* 作为现在这套 Range 的新家。
我的判断
这是那种改动很小、影响很慢的稳定化。今天你不会因为它少写多少代码,但它修掉的是一个长期的设计妥协:
「既能当迭代器又能复制」在类型层面就是矛盾的,标准库选择了不做,用户被迫拆字段。新类型把矛盾拆开 —— 可迭代(IntoIterator)和可复制(Copy)本来就不该绑在一起。
真正值得留意的信号是语法什么时候换。那一天 0..1 的类型变了,任何依赖「Range 是 Iterator」的代码(比如把 r 直接传给接受 impl Iterator 的函数)都会开始报错。到那时再改,比现在有个心理准备要好。

评论
…