sharksforarms / deku

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

Making ctx mutable provides more flexibility. #490

Open linw1995 opened 1 month ago

linw1995 commented 1 month ago
#[derive(Debug, Default, DekuRead, DekuWrite)]
#[deku(ctx = "names: &mut HashMap<String, u8>")]
pub struct DnsPacket {
    pub header: DnsHeader,
    #[deku(count = "header.qdcount", ctx = "names")]
    pub questions: Vec<DnsQuestion>,
    #[deku(count = "header.ancount", ctx = "names")]
    pub answers: Vec<DnsRecord>,
    #[deku(count = "header.nscount", ctx = "names")]
    pub authorities: Vec<DnsRecord>,
    #[deku(count = "header.arcount", ctx = "names")]
    pub additional: Vec<DnsRecord>,
}
/// DNS Question
#[derive(Debug, DekuRead, DekuWrite)]
#[deku(ctx = "mut names: &mut HashMap<String, u8>")]
pub struct DnsQuestion {
    #[deku(
        reader = "qname_read(deku::reader)",
        writer = "qname_write(deku::writer, &self.name, &mut names)"
    )]
    pub name: String,

    // ...
}

/// DNS Record
#[derive(Debug, DekuRead, DekuWrite)]
#[deku(ctx = "mut names: &mut HashMap<String, u8>")]
pub struct DnsRecord {
    #[deku(
        reader = "qname_read(deku::reader)",
        writer = "qname_write(deku::writer, &self.name, &mut names)"
    )]
    pub name: String,

    // ...
}

These two code snippets show two different usages of ctx. However, the latter works fine, while the former fails to build.

error[E0277]: the trait bound `&mut HashMap<String, u8>: std::marker::Copy` is not satisfied
  --> src/core.rs:15:20
   |
15 |     pub questions: Vec<DnsQuestion>,
   |                    ^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `&mut HashMap<String, u8>`, which is required by `Vec<core::DnsQuestion>: deku::DekuReader<'_, _>`
   |
   = help: the following other types implement trait `deku::DekuReader<'a, Ctx>`:
             `Vec<T>` implements `deku::DekuReader<'a, (Limit<T, Predicate>, Ctx)>`
             `Vec<T>` implements `deku::DekuReader<'a, Limit<T, Predicate>>`
   = note: `std::marker::Copy` is implemented for `&HashMap<String, u8>`, but not for `&mut HashMap<String, u8>`
   = note: required for `Vec<core::DnsQuestion>` to implement `deku::DekuReader<'_, (Limit<core::DnsQuestion, _>, &mut HashMap<String, u8>)>`

The former approach doesn't require adding a reader and writer. So, if ctx is mutable instead of using the Copy trait, it allows for writing simpler and more straightforward code.