How do I implement a Copy Trait for a Vec

I'm hoping someone can help me with this example because I will use the pattern often.

The sample code at the bottom of this post compiles, runs and produces the desirable output.

The method Bar::store_do_work_restore demonstrates the pattern and I would like to implement using proper rust approaches but I do not know how.

The basic idea of "store_do_work_restore" is that it saves off the member variable foos: Vec<&Foo>, replaces it with the passed in "other: Vec<&Foo>" does some work (represented by the method show()) and then restores the original contents and returns. I would like to improve this code using three single line assignments (shown in the comments) rather than the loops as shown (below each comment).

For example, the commented line:

    //let save_foos: Vec<&Foo> = self.foos;

Is my preferred approach however will not compile and produces the following error when "uncommented"

36 |         let save_foos: Vec<&Foo> = self.foos;
   |                                    ^^^^^^^^^
   |                                    |
   |                                    move occurs because `self.foos` has type `Vec<&Foo<'_>>`, which does not implement the `Copy` trait
   |                                    help: consider borrowing here: `&self.foos`

error: aborting due to previous error

(The suggestion to consider borrowing is of little help btw)

The loop that follows is how I must implement the code since I do not know how to fix this error.

It would be very helpful if someone who knows how to do this would educate me.

When you run the code this is the correct output:

bar(main)=Bar { foos: [Foo { num: 393, name: "Pretzel" }, Foo { num: 23, name: "Theo" }] }
bar(doing work)=Bar { foos: [Foo { num: 23, name: "Theo" }, Foo { num: 2928, name: "Kuno" }, Foo { num: 9393, name: "Birdie" }] }
bar(main)=Bar { foos: [Foo { num: 393, name: "Pretzel" }, Foo { num: 23, name: "Theo" }] }

The basic idea is that the "doing_work" line shows the temporary vector that has been copied in using loops, but I would prefer the code to use single line assignments.

Here's the sample code:

#[derive(Debug)]
struct Foo<'a> {
    num: i32,
    name: &'a str,
}

static FOOS: [Foo; 4] = [
    Foo{
        num: 23,
        name: "Theo",
    },
    Foo{
        num: 2928,
        name: "Kuno",
    },
    Foo{
        num: 393,
        name: "Pretzel",
    },
    Foo{
        num: 9393,
        name: "Birdie",
    },
];

#[derive(Debug)]
struct Bar<'a> {
    foos: Vec<&'a Foo<'a>>,
}
impl <'a> Bar <'a> {
    fn show(&self, label: &str) {
        println!("bar({})={:?}", label, self);
    }
    
    fn store_do_work_restore<'b>(&mut self, other: &'a Vec<&Foo>) {
        //let save_foos: Vec<&Foo> = self.foos;
        // above gives compiler error about Copy trait
        
        // this block does the work I want instead
        let mut save_foos: Vec<&Foo> = Vec::new();
        for foo in self.foos.iter() {
            save_foos.push(foo);
        }
        
        //self.foos = *other;
        // Same issue here
        self.foos.clear();
        for foo in other.iter() {
            self.foos.push(foo);
        }
        
        self.show("doing work");
        
        //self.foos = save_foos;
        // same issue here.
        self.foos.clear();
        for foo in save_foos.iter() {
            self.foos.push(foo);
        }
    }
}

fn main() {
    let mut bar = Bar {
        foos: vec![&FOOS[2], &FOOS[0]],
    };
    
    bar.show("main");
    
    let other = &vec![&FOOS[0], &FOOS[1], &FOOS[3]];
    bar.store_do_work_restore(other);
    
    bar.show("main");
}

You can't Copy, but you can use vec.clone() for your single-line assignment.

1 Like

Great!

So I just tried:
let save_foos: Vec<&Foo> = self.foos.clone();

And this worked! Thanks so much!

However, what if I had several other vectors inside of Bar. This would require a clone for each. Is there a way to implement the Copy Trait so I can "hide" (encapsulate) all of that detail?

In Rust Copy has a specific meaning of duplicating bytes without doing any additional bookkeeping. Vec is fundamentally incompatible with this, because it owns heap-allocated storage, which must have only one and exactly one owner. If it was allowed to be Copy, it'd be unclear which of the copies is the last one to free the storage.

Slice &[] can be Copy, because it doesn't free the storage.

But if the compiler is telling you to make something Copy, that's just a suggestion. You don't have to do this. You may have just misunderstanding of ownership, and you could avoid the need to copy by reorganizing your code, e.g. using shared references, a different data structure, or moving values to different structs or scopes. It depends on the situation.

5 Likes

I understand the ownership here.

Each Foo owns it's number and str ref, and each Bar owns a vector of Foo references. The Foos themselves are owned by the static array. My goal for the "store_do_work_restore()" method is to save off the Bar owned vector so it does not get clobbered by the other work that I intend to do against the passed in "other" vector (represented by the show() function) then restore it to it's original state before returning.

So I perhaps am not asking the right question and agree that the Copy trait is probably a distraction.

If that's all true then perhaps I'm just asking how to overload the assignment operator. I can research that if that's the correct answer.

There's also no way to overload the assignment operator. Rust's copies and moves (including assignment) are always semantically just the same as a memcpy of the immediate bytes. If you want something more complex, it will have to be explicit.

3 Likes

By the way. On a different topic. It took me several iterations of compiling and adjusting the lifetime specifiers before I go it to work. In particular, I found it a surprise that I needed to pick a different name for the lifetime 'b on the fn store_do_work_restore<'b> method.

It may be me, but I've yet to find a really comprehensive (easy to follow) educational document that would help me understand that!

If you make your types owning rather than borrowing, deep clones will become automatic, since containers' Clone is usually defined by element-wise cloning.

I couldn't see why the <'b> was needed so I copied your code and removed it from store_do_work_restore, and it still compiles and works for me. Maybe it was an intermediate issue as you were editing?

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.