Search code examples
vectorenumsrustmove

Rust match mutable enum reference with vectors


I'm trying to change an enum's named property but getting this error.

cannot move out of a mutable referencerustc(E0507)
parser.rs(323, 36): data moved here
parser.rs(323, 36): move occurs because `statements` has type `std::vec::Vec<std::boxed::Box<ast::ast::StatementType>>`, which does not implement the `Copy` trait

I saw that we can change enum's named props with match statements. But I couldn't understand why there's a move occurrence, since I'm borrowing the enum itself. Here's the code:

        match &mut block {
            StatementType::Block { mut statements, .. } => {
                statements = block_statements;
            },
            _ => panic!()
        };
        return block;

I've tried mem::swap too but still it's the same error:

        match &mut block {
            StatementType::Block { mut statements, .. } => {
                // statements = block_statements;
                std::mem::swap(&mut statements, &mut block_statements);
            },
            _ => panic!()
        };
        return block;

BUT when I do this:

                std::mem::swap(&mut *statements, &mut *block_statements);

The error changes to:

the size for values of type `[std::boxed::Box<ast::ast::StatementType>]` cannot be known at compilation time

doesn't have a size known at compile-time

Types are:

  • StatementType is an enum that derives Clone
  • Block is mutable variable of StatementType
  • Block's statements is a variable of Vec<Box<StatementType>>
  • block_statements is another variable of Vec<Box<StatementType>>

Please do not say that it happens because statements' type is Vector: come with a solution as I can read error messages too.


Solution

  • You have to think what the type of statements is and what you would like it to be.

    With the code as you wrote it, it is of type Vec<_> (sorry, I said it), but since the match captures the block by reference, it cannot take the contents by value, hence the error. Note that the error is not in the assignment but in the match brace itself:

    error[E0507]: cannot move out of a mutable reference
      --> src/main.rs:15:11
       |
    15 |     match &mut block {
       |           ^^^^^^^^^^
    16 |         StatementType::Block { mut statements, .. } => {
       |                                --------------
       |                                |
       |                                data moved here
       |                                move occurs because `statements` has type `std::vec::Vec<std::boxed::Box<StatementType>>`, which does not implement the `Copy` trait
    

    You would like statement to be of type &mut Vec<_> of course. And you get that by using the ref mut capture mode:

        match block {
            StatementType::Block { ref mut statements, .. } => {
                *statements = block_statements;
            },
            _ => panic!()
        };
    

    And remember to use *statement when assigning, as it is now a reference. You could also use a mem::swap if you want, of course:

                std::mem::swap(statements, &mut block_statements);
    

    But note that you do not need to match &mut block but you can do match block directly.

    There is this thing called match ergonomics that lets you match against a reference and omit the ref mut capture mode, that makes your code easier to write and understand:

        match &mut block {
            StatementType::Block { statements, .. } => {
                *statements = block_statements;
            },
            _ => panic!()
        };
    

    The problem in your original code is that if specify any capture mode then match ergonomics is disabled.