How can I modify some struct behind an enum?

Hi there,

I'm trying to modify some (value in a) struct provided in an enum. Simplified Example:

fn main() {

    let enums = vec![
        Enum::A(Struct {number: 1}, Struct {number: 0}),
        Enum::B(Struct {number: 2}, Struct {number: 0})
    ];
    
    for e in enums {
        e.update_output();
    }
}

struct Struct {
    number: i32
}

enum Enum {
    A(Struct,Struct),
    B(Struct,Struct)
}
    
impl Enum {
    fn update_output(&self) -> &Self {
        match self {
            Enum::A(input, mut output) => {
                output.number = input.number;
            },
            Enum::B(input, mut output) => {
                output.number = input.number;
            }
        }
        
        self
    }
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0507]: cannot move out of `self` as enum variant `B` which is behind a shared reference
  --> src/main.rs:24:15
   |
24 |         match self {
   |               ^^^^
25 |             Enum::A(input, mut output) => {
   |                            ---------- data moved here
...
28 |             Enum::B(input, mut output) => {
   |                            ---------- ...and here
   |
   = note: move occurs because these variables have types that don't implement the `Copy` trait

For more information about this error, try `rustc --explain E0507`.
error: could not compile `playground` due to previous error

How can I achieve this? Thanks in advance!

update_output() needs to take &mut self.

1 Like

This requires me to also use mut in the for loop. But the outcome is still the same:

Now, changing Enum::A(input, mut output) to Enum::A(input, output) (and the other pattern similarly) will make the code compile.

The -> &Self return value still seems somewhat weird. Either remove it, or - if method chaining is wanted - return &mut Self.

For explanation as to why the Enum::A(input, output) works: This pattern matched against a &mut Enum instead of Enum will bind input and output to &mut Struct instead of Struct accordingly.


There is an equivalent but older (as in supported in Rust for a longer time; not actually outdated) way to write the same pattern in Rust that some people still prefer, which looks like &mut Enum::A(ref mut input, ref mut output)

match self {
            &mut Enum::A(ref mut input, ref mut output) => {
                output.number = input.number;
            },
            …
}

or alternatively, people might like to write

match *self {
            Enum::A(ref mut input, ref mut output) => {
                output.number = input.number;
            },
            …
}

where ref mut variable is the explicit way to create a &mut Struct variable instead of (trying to) move the Struct value itself.

I’m not recommending you to necessarily use these alternative syntaxes, but it can be useful to have seen them for developing a better understanding of how patterns operate and what exactly the kind of “magic” is that is involved in matching a &mut SomeEnum against a SomeEnum::Variant(variable, …) pattern.

3 Likes

I figured that out in the meantime, thanks a lot!

But this makes all of the the things of the enum mutable. In reality i also want to do some shifting in those branches, which is not implemented for mutable things:

Any ideas?

Yeah, to get back to the value behind the mutable reference, just dereference it: input.number << *shift.


Or, if using the alternative approach, you could avoid creating a reference for the shift field in the first place, as in something like e.g.

match *self {
    Enum::A(ref input, ref mut output) => {
        output.number = input.number;
    },
    Enum::B(ref input, shift, ref mut output) => {
        output.number = input.number << shift;
    }
}
1 Like

I pushed the error to somewhere else and I need to think about it - but your hints were very helpful, thanks a lot!

Even though it doesn't resemble with the original question 100% I reply here once more with an exended (striped down) example of what I want to achieve:

The error is still that I "cannot move out of self". Are you able to enlighten me once more?

I also have a feeling that wrapping vecs in structs to work with them is kind of un-ideomatic, maybe someone can comment on that as well?

It's perfectly fine to put any type in a struct. Your code compiles with &mut self.0. (you don't want the iterator impl for the vector by-value, you want to iterate by mutable reference.)

2 Likes

Don't worry about asking too many questions. Reasoning about what syntax will borrow or move what value or variable in what way is somewhat nontrivial, but I'm sure you'll quickly get a better feeling for what kind of approaches you can try from the answers to questions like these.

I also feel like some of these cases, e. g. the idea to add * dereferencing to make the shift operation work, or to try not only the already suggested &self.0 but perhaps also &mut self.0 are things that the compiler could suggest better / be more helpful with, i. e. I see room for improvement in diagnostics.

2 Likes

Thanks again, it indeed is working now :slight_smile: