Question copy Vec<T>

Hi,

I have the following piece of code

#[derive(Debug, Copy, Clone)]
struct Float(f64);

#[derive(Debug)]
struct Series {
    data: Vec<Float>,
}

impl IntoIterator for Series {
    type Item = Float;
    type IntoIter = std::vec::IntoIter<Self::Item>;
    
    fn into_iter(self) -> Self::IntoIter {
        self.data.into_iter()
    }
}

impl From<&str> for Series {
    fn from(s: &str) -> Self {
        let mut data = Vec::new();
        let vs: Vec<&str> = s.split(" ").collect();
        for v in vs.iter() {
            let v: f64 = v.parse().unwrap();
            data.push(Float(v));       
        }
        Series{data: data}    
    }
}

fn main() {
    let s1 = Series::from("1 2 3.4");
    println!("First element => {:?}", s1.into_iter().next());
    println!("{:?}", s1);
}

The code doesn't compile for the following reason

move occurs because s1 has type Series, which does not implement the Copy trait

Looking at the docs I understand that a type can only implement Copy if all its components can implement copy.

So in my case Vec<T> cannot implement copy.

Can someone explain to me (rather new to programming)

  1. why Copy cannot be implemented on Vec<T>
  2. how I have to change my code so I could call next and then use the remainder of Series in another place of my code

Thanks for your help!

For a type to be Copy, it must be valid to clone it by simply copying over the bytes it consists of. Be aware that the way a vector is laid out is like this:

+------------+
| The vector |
+------------+
      |
      V
+--------+--------+--------+-------------------------+
| Item 1 | Item 2 | Item 3 | Additional capacity...  |
+--------+--------+--------+-------------------------+

The important part is that the bytes that Copy is referring to is only the box I marked with “The vector”. The heap allocation that it has a pointer to is not part of this copy. The issue is that a vector has ownership of this heap allocation — you can't have two that point to the same, since this could cause all sorts of issues: What if one copy of the vector added an item and it ran out of capacity, so it had to reallocate the memory? Then the other vector would suddenly be looking at deallocated memory! Boom!

Of course, the vector is still Clone. This trait simply means that it can be cloned somehow. Of course this involves cloning every element in the vector, but it is possible.

As for how to solve your issue, the problem is that std::vec::IntoIter takes ownership of the vector. Of course, you could just clone the series before calling into_iter(), but a better solution is to borrow the collection instead:

impl Series {
    pub fn iter(&self) -> std::slice::Iter<Float> {
        self.data.iter()
    }
}

Although this is now an iterator over values of type &Float. Since Float is copy, you might want to copy them:

impl Series {
    pub fn iter(&self) -> std::iter::Copied<std::slice::Iter<Float>> {
        self.data.iter().copied()
    }
}
// or hide the exact kind of iterator...
impl Series {
    pub fn iter<'a>(&'a self) -> impl Iterator<Item = Float> + 'a {
        self.data.iter().copied()
    }
}

It's also common to implement IntoIterator for references to collections, e.g.

impl<'a> IntoIterator for &'a Series {
    type Item = Float;
    type IntoIter = std::iter::Copied<std::slice::Iter<'a, Float>>;
    
    fn into_iter(self) -> Self::IntoIter {
        self.data.iter().copied()
    }
}

This allows using &series in a for loop.

10 Likes

@alice Thanks you very much for this answer. This helps a lot!!!

One question regarding the solution where hiding the exact kind of iterator: what is a use case for this?

The impl Trait syntax which hides the exact type has a few uses:

  1. Sometimes the types of things become very cumbersome to write out, so it's easier to just say what trait it implements.
  2. You may not want to promise that you return that exact type of iterator for backwards compatibility reasons.
  3. When closures get involved, you lose the ability to write out the type — the type is unnameable, so you have to use impl Trait.

Don't forget 4 (or maybe 3b, since it is still the same issue of not knowing the type):

  1. You need to return a value which was returned from another function that uses impl Trait.
1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.