Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to access an enum variant's field without explicitly binding it during match?

Tags:

rust

Lets say I have an enum E, that might be auto-generated or outside of my control, with many variants and each variant having a number of fields.

enum E {
    A {
        x1: u8,
        x2: u8,
        x3: u8,
        // ...
        x9: u8,
    },
    B,
}

In reality the fields might be longer and neither nice to remember, nor nice to type.

I now want to write functions that operate on (variants of) E. However, I am also lazy, and I don't want to repeat myself, declaring each used field explicitly when destructuring the enum*.

Intuitively I would have expected the binding operator @ to do the job here, but it only binds the whole enum e, not the given variant E::A.

What is the shortest / most elegant way to achieve the following intention?

fn f(e: &E) {
    match e {
        bad @ E::A { .. } => dbg!(bad.x1),
        _ => {}
    }
}

*Update since this has been brought up in two answers already, I do not want to match with E::A { x1, .. }, as this gets tedious when needing multiple fields with long names. In the example below, I would have to type out some_other_field_with_an_impossibly_long_name twice in my own code (once when binding it, once when using it), whereas in the hypothetical bad @ E::A case I'd have to type it out only once.

match e {
    E::A { some_field_with_a_long_name, some_other_field_with_an_impossibly_long_name, yet_another_field, .. } => dbg!(some_other_field_with_an_impossibly_long_name),
    _ => {}
}
like image 829
left4bread Avatar asked Dec 31 '19 14:12

left4bread


2 Answers

I think the following could help:

fn f(e: &E) {
  match e {
   E::A {x1, .. } => {dbg!(x1);},
    _ => {}
  };
}

E::A {x1,..} is short for bad @ E::A {x1:x1, ..} and binds the value of bad.x1 to a new local variable x1 that is available in the body scope.

like image 147
CoronA Avatar answered Nov 15 '22 06:11

CoronA


You may use a macro with variadic args, so the compiler does the typing long names twice, and it binds as many as parameters you need. You may call your function inside the macro instead of println:

    f!(&e, x1);
    f!(&e, x2, x1);
macro_rules! f {
    ($e: expr, $( $name:ident ),+ ) => {
        match $e {
            E::A {   $($name),* , ..} => {
                println!("{:?}", &[$($name),*]); // call your function here.
            }
            _ => {}
        }
    };
}

Try it on the Rust Playground

like image 21
wasmup Avatar answered Nov 15 '22 08:11

wasmup