Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't ops::Range<T> implement Copy, even if T is Copy?

Tags:

rust

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?

like image 206
Lukas Kalbertodt Avatar asked Apr 14 '17 18:04

Lukas Kalbertodt


People also ask

Which does not implement the copy trait?

A String is a type that does not implement the Copy trait.

Does Option implement copy?

Option<&mut T> is not copyable or clonable, because if it was, it'd allow a user to create more than one mutable borrow to the same data. This breaks rusts current borrowing axioms.

How do you implement a clone in Rust?

To easily implement the Clone trait, you can also use #[derive(Clone)] . Example: #[derive(Clone)] // we add the Clone trait to Morpheus struct struct Morpheus { blue_pill: f32, red_pill: i64, } fn main() { let f = Morpheus { blue_pill: 0.0, red_pill: 0 }; let copy = f. clone(); // and now we can clone it! }

How do you copy variables in rust?

You can just use . clone() for your duplicate function.


1 Answers

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:

  • Why are iterators not copyable?
  • Re-using a range for iteration
like image 183
Shepmaster Avatar answered Sep 17 '22 14:09

Shepmaster