#234 Jul 1, 2026

234. core::range::Range — A Range That's Copy, So You Can Store and Reuse It

The classic 0..3 isn’t Copy — it is the iterator, so it gets consumed and can’t live in a Copy struct. Rust 1.96 stabilises core::range::Range, which is Copy and iterates through IntoIterator instead.

Why the old Range fights you

std::ops::Range implements Iterator directly. That’s convenient in a for loop, but it means the range is mutable iterator state, so it can’t be Copy:

1
2
3
let r = 0..3;
let total: i32 = r.sum();   // moves r
// let n = r.count();       // error: use of moved value: r

It also can’t sit inside a Copy type, which is annoying when you want a lightweight span:

1
2
#[derive(Clone, Copy)]        // error: the trait Copy is not
struct Span(std::ops::Range<usize>);  // implemented for Range<usize>

The new core::range::Range is Copy

Stabilised in 1.96, core::range::Range implements IntoIterator rather than Iterator, which frees it to be Copy. Convert a literal range into it with .into():

1
2
3
4
5
6
7
use core::range::Range;

let r: Range<usize> = (0..3).into();
let total: usize = r.into_iter().sum();   // r is Copy, not moved
let count = r.into_iter().count();        // still usable
assert_eq!(total, 3);
assert_eq!(count, 3);

Now it fits in a Copy struct

Because it’s Copy, you can store a range as a cheap, copyable span and still index slices with it directly:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
use core::range::Range;

#[derive(Clone, Copy)]
struct Span(Range<usize>);

impl Span {
    fn of(self, s: &str) -> &str { &s[self.0] }
}

let span = Span((2..5).into());
let a = span;                 // copies, no move
assert_eq!(a.of("hello world"), "llo");
assert_eq!(span.of("hello world"), "llo");   // span still valid

No more splitting a range into separate start / end fields just to keep a struct Copy.

Heads up

0..3 syntax still produces the legacy std::ops::Range for now — the new types come in via .into() or by naming core::range::Range. A future edition will switch the syntax over. For public APIs that should accept either, take impl RangeBounds<usize>.

Stabilised in Rust 1.96 (May 2026).

← Previous 233. str::split_terminator — Split Without the Trailing Empty String Next → 235. Iterator::rposition — Find the Last Match Without Reversing-and-Subtracting