sharksforarms / deku

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

Using Traits within `ctx` and `endian` fails for some cases #332

Closed wcampbell0x2a closed 1 year ago

wcampbell0x2a commented 1 year ago

This code with a struct generic type parameter

use deku::prelude::*;

trait Nice {}

struct Kind<N: Nice> {
    n: N,
    endian: deku::ctx::Endian,
}

#[derive(DekuRead)]
#[deku(ctx = "kind: Kind<N>")]
#[deku(endian = "kind.endian ")]
struct Test {
    whatever: u8,
}

fn main() {
}

Generates this:

$ cargo expand
![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
use deku::prelude::*;
trait Nice {}
struct Kind<N: Nice> {
    n: N,
    endian: deku::ctx::Endian,
}
#[deku(ctx = "kind: Kind<N>")]
#[deku(endian = "kind.endian ")]
struct Test {
    whatever: u8,
}
impl ::deku::DekuRead<'_, Kind<N>> for Test {
    fn read(
        __deku_input_bits: &'_ ::deku::bitvec::BitSlice<u8, ::deku::bitvec::Msb0>,
        kind: Kind<N>,
    ) -> core::result::Result<
        (&'_ ::deku::bitvec::BitSlice<u8, ::deku::bitvec::Msb0>, Self),
        ::deku::DekuError,
    > {
        use core::convert::TryFrom;
        let mut __deku_rest = __deku_input_bits;
        let __deku_whatever = {
            let (__deku_new_rest, __deku_value) = <u8 as ::deku::DekuRead<
                '_,
                _,
            >>::read(__deku_rest, (kind.endian))?;
            let __deku_value: u8 = core::result::Result::<
                _,
                ::deku::DekuError,
            >::Ok(__deku_value)?;
            __deku_rest = __deku_new_rest;
            __deku_value
        };
        let whatever = &__deku_whatever;
        let __deku_value = Self { whatever: __deku_whatever };
        Ok((__deku_rest, __deku_value))
    }
}
fn main() {}

Should be this:

impl<N: Nice> ::deku::DekuRead<'_, Kind<N>> for Test {
    fn read(
        __deku_input_bits: &'_ ::deku::bitvec::BitSlice<u8, ::deku::bitvec::Msb0>,
        kind: Kind<N>,
    ) -> Result<
        (&'_ ::deku::bitvec::BitSlice<u8, ::deku::bitvec::Msb0>, Self),
        ::deku::DekuError,
    > {
        use core::convert::TryFrom;
        let mut __deku_rest = __deku_input_bits;
        let __deku_whatever = {
            let (__deku_new_rest, __deku_value) = <u8 as ::deku::DekuRead<
                '_,
                _,
            >>::read(__deku_rest, (kind.endian))?;
            let __deku_value: u8 = Result::<_, ::deku::DekuError>::Ok(__deku_value)?;
            __deku_rest = __deku_new_rest;
            __deku_value
        };
        let whatever = &__deku_whatever;
        let __deku_value = Self { whatever: __deku_whatever };
        Ok((__deku_rest, __deku_value))
    }
}
fn main() {}
wcampbell0x2a commented 1 year ago

Actually not sure how to solve this. Looks like the impl<N: Nice> is required at the beginning, but deku has no way of knowing that bound.

wcampbell0x2a commented 1 year ago

Nevermind, struct Test<N : Nice> { fixes this.

wcampbell0x2a commented 1 year ago

This isn't an issue with deku, just noting you need to use PhantomData for this to work:

use std::marker::PhantomData;

use deku::prelude::*;

trait Nice {}

struct Kind<N: Nice> {
    n: N,
    endian: deku::ctx::Endian,
}

#[derive(DekuRead)]
#[deku(ctx = "kind: Kind<N>")]
#[deku(endian = "kind.endian ")]
struct Test<N: Nice> {
    whatever: u8,
    #[deku(skip)]
    phantom: PhantomData<N>,
}

fn main() {
}

Otherwise you get this error!:

error[E0392]: parameter `N` is never used
  --> src/main.rs:13:13
   |
13 | struct Test<N: Nice> {
   |             ^ unused parameter
   |
   = help: consider removing `N`, referring to it in a field, or using a marker such as `PhantomData`

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