28

Recently, I wanted to write a type holding parameters for a 3D projection:

use std::ops::Range;

#[derive(Clone, Copy)]
struct CamProj {
    /// Near and far plane
    proj_range: Range<f32>,
    /// Field of view
    fov: cgmath::Rad<f32>,     // `Rad` derives `Copy`
    /// Width divided by height
    aspect_ratio: f32,       
}

However, I got this error:

error[E0204]: the trait `Copy` may not be implemented for this type
 --> <anon>:3:21
  |
3 |     #[derive(Clone, Copy)]
  |                     ^^^^
...
6 |         proj_range: Range<f32>,
  |         ---------------------- this field does not implement `Copy`

So apparently, Range<T> never implements Copy, even if T is Copy, like f32 is. Why is that? I thought a Range<T> would just be a pair of Ts? So it surely could implement Copy?

Lukas Kalbertodt
  • 79,749
  • 26
  • 255
  • 305
  • 2
    I'm sure I've seen this question before... but of course cannot find it again. The closest potential duplicate I found was http://stackoverflow.com/questions/31045637/re-using-a-range-for-iteration. I seem to recall it was not implemented because there were cases where it could be confusing: https://github.com/rust-lang/rust/issues/18045 – Matthieu M. Apr 14 '17 at 18:21

1 Answers1

30

Because Range<T> is often used as an iterator, and having iterators be Copy was discovered to be a footgun. One specific example had to do with thinking that an iterator was advanced, when in reality it was a copy that was advanced:

for x in it {  // a *copy* of the iterator is used here
    // ..
}

match it.next() {  // the original iterator is used here
    // ..
}

Another example:

fn main() {
    let stream = "Hello, world!".chars().cycle();
    for _ in 0..10 {
        let chunk: String = stream.take(3).collect();
        println!("{}", chunk);
    }
}

And another that prompted a question: Using the same iterator multiple times in Rust

It was believed that having iterators be explicitly copied via clone helped prevent these cases


Specifically re-adding Copy to Range was proposed and rejected. A potential workaround was suggested:

Range fields are public, you can repack them into a copyable tuple (or equivalent) at the constructor/function boundary

See also:

Community
  • 1
  • 1
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366