sharksforarms / deku

Declarative binary reading and writing: bit-level, symmetric, serialization/deserialization
Apache License 2.0
1.14k stars 55 forks source link

How to access enum-struct members? #410

Closed sempervictus closed 10 months ago

sempervictus commented 10 months ago

How would one access the fields from Var4 in https://github.com/sharksforarms/deku/blob/master/examples/enums.rs#L42 (let somevar = deku_test.field_a; doesn't fly since the compiler isn't sure its of type Var4)? I have a struct consisting of a header and body, the body is an enum in which every variant contains a different parsing semantic for the specific data type. Getting at that data however is proving mildly difficult :smile:

wcampbell0x2a commented 10 months ago

You can use if let for this: https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html. Unless I read your problem incorrectly.

if let DekuTest::Var4 { field_a, field_b } = &deku_test {
    println!("{field_a:?} {field_b:?}");
}
sempervictus commented 10 months ago

I see, sorry was thinking about this the wrong way - have multiple arms there so trying to write this as a matcher, but this seems to do the trick - thank you!

sempervictus commented 10 months ago

@wcampbell0x2a: accessing for reads does work this way, but changes to mutable elements do not.

Even manually assigning the vectors resulting from a changed element in nested access does not alter the contents of packet in this code or even selected - the s.val i'm setting to 0 here is still 1 when spat out from the debug_println! macro:

let mut data_pkt = Data::try_from(&packet.data[..]).unwrap();
match AdditionalOptions::try_from(&data_pkt.data[..]) {
    Ok(ao) => {
        for service in &ao.svcs {
            if let SvcBody::ENCRYPT { version: _, algos: _, selected } = service.svc {
                match selected {
                    Some(mut s) => {
                        s.val = 0;
                    }
                    None => {}
                };
                debug_println!("{:?}", &selected);
            }
        };
        debug_println!("{:?}", &ao);
        data_pkt.data = ao.try_into()?;
        packet.data = data_pkt.try_into()?;
    },
    Err(_) => {}
}
sempervictus commented 10 months ago

This probably warrants docs all around, but getting changes to persist is far from straightforward. The mutable iterator requirement is normal Rust borrow checker stuff, but persisting the value of selected requires a mutable ref...


let mut ao = AdditionalOptions::try_from(c.data.as_ref())?;
for service in &mut ao.svcs {
    if let SvcBody::ENCRYPT { version: _, algos: _, ref mut selected } = service.svc {
        match *selected {
            Some(_s) => {
                *selected = None;
                service.hdr.sub_packets = service.hdr.sub_packets - 1;
            }
            None => {}
        };
    }
};