Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I move out of a struct field that is an Option?

I want to collect changes to a struct and apply them all at once. The basic outline looks like this:

enum SomeEnum {
    Foo,
    Bar,
}

struct SomeStruct {
    attrib: SomeEnum,
    next_attrib: Option<SomeEnum>,
}

impl SomeStruct {
    pub fn apply_changes(&mut self) {
        if let Some(se) = self.next_attrib {
            self.attrib = se;
        }
        self.next_attrib = None;
    }
}

which yields the following compiler error:

error[E0507]: cannot move out of borrowed content
  --> src/lib.rs:13:27
   |
13 |         if let Some(se) = self.next_attrib {
   |                     --    ^^^^ cannot move out of borrowed content
   |                     |
   |                     hint: to prevent move, use `ref se` or `ref mut se`

I found Get an enum field from a struct: cannot move out of borrowed content and added #[derive(Clone, Copy)] to my enum's definition.

This may work but I feel uncomfortable about (implicitly) using copying since this could generally happen to larger datatypes as well.

The actual owner is never moved out of the struct.

Is there another way to accomplish this, without exposing the Copy/Clone traits to all users of the enum?

like image 639
Burdui Avatar asked Aug 26 '18 23:08

Burdui


Video Answer


1 Answers

Essentially, you can't assign the value to self.attrib if it's still owned by self.next_atrrib. That means you need to remove the value from self.next_attrib and then give ownership to self.attrib.

One way to do this would be to manually replace the value. For instance, you could use std::mem::replace to replace the value with None and take ownership of the current value as next_attrib. Then you can take the value and, if it is Some(_), you can place its content in self.attrib.:

impl SomeStruct {
    pub fn apply_changes(&mut self) {
        let next_attrib = std::mem::replace(&mut self.next_attrib, None);
        if let Some(se) = next_attrib {
            self.attrib = se;
        }
    }
}

Since this is a relatively common pattern, however, there is a utility function on Option to handle situations where you'd like to take ownership of the contents of an Option and set the Option to None. The Option::take method is what you want.

impl SomeStruct {
    pub fn apply_changes(&mut self) {
        if let Some(se) = self.next_attrib.take() {
            self.attrib = se;
        }
    }
}

See also:

  • How can I swap in a new value for a field in a mutable reference to a structure?
like image 52
loganfsmyth Avatar answered Oct 12 '22 00:10

loganfsmyth