Transforming (consume+set) struct property

How can you change a property of a struct with a value resulted from the mutable consumption (take ownership) of the property (on the same thread)?

The best I could come up with is

struct Foo {
    arr: Vec<u8>,

impl Foo {
    fn change(&mut self) {
        self.arr = self.arr.into_iter().collect();

but this will create an additional default item :frowning_face:

The Default implementation for Vec<T> calls Vec::new, which is basically free (doesn't allocate any memory), so at least if your field is a Vec I wouldn't worry about this. Default implementations for other types are often comparably cheap.

Also note your change could be a method on Foo taking &mut self—it doesn't need to take ownership of its argument.


You're asking for something like this. The general problem is that if you take out of the reference, then panic while constructing a replacement, you need a default object in place in order to avoid a double-free during the stack unwinding. So you must either create a default value, or decide that you will abort on panic rather than unwind.


Yet another solution is to wrap the type inside an Option and use Option::take to steal the Some-valued field, leaving a None in its place. This is basically a one-stop shop for the "default object" pattern, even for non-Default types.

1 Like

In this case you can just move out of arr :slightly_smiling_face::

fn change(mut foo: Foo) {
    foo.arr = foo.arr.into_iter().map(...).collect();

The real case my be more complicated, of course.

1 Like

Unfortunately you cant move arr. I updated the example so it's more clear now.

It basically says:

error[E0507]: cannot move out of `self.arr` which is behind a mutable reference
  --> src/runtime/worker/
70 |         self.arr = self.arr.into_iter().collect();
   |                    ^^^^^^^^ move occurs because `self.arr` has type `Vec<u8>`, which does not implement the `Copy` trait

Apparently it's possible by using a third party module, but it comes with a cost, check @skysch's answer. As I am new to Rust I would rather just use standard features.

As pointed out by @cole-miller creating an empty vector with new is rather inexpensive, so I will go with this.

Another option would be to use Option (as pointed out by @H2CO3), but it would mean checking every time for None.

Thank you everyone for your help!

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.