sharksforarms / deku

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

Implement missing contextual items to have a better compile time error message #107

Open sharksforarms opened 4 years ago

sharksforarms commented 4 years ago

i.e.

For,

 Diagnostics:
 1. the trait bound `std::vec::Vec<u8>: deku::DekuRead<deku::ctx::BitSize>` is not satisfied
 the following implementations were found:
   <std::vec::Vec<T> as deku::DekuRead<(deku::ctx::Count, Ctx)>>
   <std::vec::Vec<T> as deku::DekuRead<deku::ctx::Count>>
 required by `deku::DekuRead::read`

The error message is

 Diagnostics:
 1. the trait bound `std::vec::Vec<u8>: deku::DekuRead<deku::ctx::BitSize>` is not satisfied
 the following implementations were found:
   <std::vec::Vec<T> as deku::DekuRead<(deku::ctx::Count, Ctx)>>
   <std::vec::Vec<T> as deku::DekuRead<deku::ctx::Count>>
 required by `deku::DekuRead::read`

Which, without knowledge of Deku internals, is not helping the user.

A better error message might be "bits not supported on Vec. Did you mean count?"

Task: impl missing contexts for types and give helpful error message as a panic!

MrNbaYoh commented 3 years ago

A good first step would be to add bounds to DekuRead & DekuWrite for every type parameters in structures that derive those traits. The compiler would then indicate which specific field does not derive the proper traits. Currently when a type does not derive the appropriate trait, an error is thrown because they're needed by the read & write functions, and the compiler points to the DekuRead/DekuWrite span in the derive macro.

sharksforarms commented 3 years ago

Hey @MrNbaYoh thanks for the suggestion!

Experimenting with the following, is this what you mean?

trait DekuRead {
}

impl DekuRead for u8 {
}

struct Test {
    field_a: u8,
    field_b: u16,
}

#[allow(non_camel_case_types)]
trait DekuContainerRead<field_a: DekuRead = u8, field_b: DekuRead = u16> {
}

impl DekuContainerRead for Test {
}
error[E0277]: the trait bound `u16: DekuRead` is not satisfied
  --> examples/out.rs:14:58
   |
14 | trait DekuContainerRead<field_a: DekuRead = u8, field_b: DekuRead = u16> {
   | ---------------------------------------------------------^^^^^^^^-------
   | |                                                        |
   | |                                                        the trait `DekuRead` is not implemented for `u16`
   | required by `DekuContainerRead`

error[E0277]: the trait bound `u16: DekuRead` is not satisfied
  --> examples/out.rs:17:6
   |
14 | trait DekuContainerRead<field_a: DekuRead = u8, field_b: DekuRead = u16> {
   |                                                          -------- required by this bound in `DekuContainerRead`
...
17 | impl DekuContainerRead for Test {
   |      ^^^^^^^^^^^^^^^^^ the trait `DekuRead` is not implemented for `u16`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0277`.
error: could not compile `deku`
sharksforarms commented 3 years ago

I'm currently looking into how the Debug derive works.... This is pretty much what you're describing I think

sharksforarms commented 3 years ago

Actually, serde does something similar which is nice:

use serde::Serialize;

struct Test2 {}

#[derive(Serialize)]
struct Test {
    field_a: u8,
    field_b: Test2,
}

fn main() {
}
error[E0277]: the trait bound `Test2: Serialize` is not satisfied
 --> src/main.rs:8:5
  |
8 |     field_b: Test2,
  |     ^^^^^^^ the trait `Serialize` is not implemented for `Test2`
  |
  = note: required by `_serde::ser::SerializeStruct::serialize_field`

error: aborting due to previous error

https://docs.serde.rs/serde/ser/trait.SerializeStruct.html