Deku for bit-level Rust serialization
Declarative binary reading and writing: bit-level, symmetric, serialization/deserialization
At a glance
- What is it?
- Deku is a Rust crate that derives binary readers and writers for structs and enums, including layouts that cross byte boundaries. It fits fixed binary formats, with generated behavior driven by field attributes rather than Rust memory layout.
- Who is it for?
- Deku is appropriate for Rust projects that define a fixed binary layout and want reading and writing generated from the same field declarations. It is less suitable for teams that require handwritten control over every conversion branch or cannot accept proc-macro attributes in their public types.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Star history
- sharksforarms/deku1,435 ★ · +2
| Repository | 2026-10-16 | 2026-10-23 |
|---|---|---|
| sharksforarms/deku | 1.4k | 1.4k |
2 Stars in the last 7 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
Why Deku targets packed binary formats
Deku addresses a specific Rust problem: describing binary structures whose fields do not follow ordinary byte-aligned layouts. A format may place two four-bit values in one byte, followed by a 16-bit integer. Implementing the conversion in both directions requires consistent rules for field order, bit width, and endianness. Deku places those rules in attributes attached to the data structure, then derives the reader and writer from that description.
The intended users are Rust developers working with binary structs, network headers, and similar formats whose positions are defined in advance. Deku also applies to enums, but the README does not provide an enum example. The repository contains `examples/enums.rs`, `examples/enums_catch_all.rs`, `examples/ipv4.rs`, and `examples/80211.rs`, which show that enum and protocol-oriented examples have dedicated files, although their contents are not documented in the README.
This is schema-driven code generation. It works best when engineers can state the binary layout explicitly with derive macros and field attributes. Deku is not described as a runtime, reflection-based object serialization framework. The visible design instead makes protocol details part of the Rust type declaration. That can help reviewers connect a wire-format definition with the application type, but it also means incorrect attributes can describe an incorrect implementation.
How DekuRead and DekuWrite share one layout
The central mechanism is the pair of derives `DekuRead` and `DekuWrite`. A struct or enum annotated with both receives code for binary input and binary output. The README describes this as symmetric reader and writer generation, intended to remove redundant and error-prone conversion code.
The main example derives both implementations and selects big-endian encoding for the structure. Two fields receive `#[deku(bits = 4)]`, telling Deku that each occupies four bits. The third field is a `u16` and uses the structure's declared endianness. Because the first two fields together fill one byte, their placement and order are part of the generated behavior rather than the native representation of the Rust struct.
Reading returns the unconsumed input alongside the decoded value. The documented call passes a byte slice and an offset, then names the returned tuple `_rest` and `val`. This API is significant for parsing a larger packet or stream: the caller receives the remaining input rather than receiving only a decoded value. The README does not explain every possible remainder and offset behavior, so developers who require strict full-buffer decoding should check that contract in the release documentation.
Writing uses `to_bytes()`. Since both operations are generated from the same declarations, a field change can affect reading and writing together. This lowers the risk of maintaining separately drifting encoders and decoders, but it does not validate that the declared schema matches an external specification.
Installing Deku and decoding a packed header
The README does not provide a separate shell installation command. It directs users to the `deku` crate on crates.io and shows dependency declaration as the setup step. For a normal Rust project using default features, add this to `Cargo.toml`:
[dependencies]
deku = "0.20"The complete README example demonstrates a round trip rather than installation plus an isolated decode call. It imports Deku's prelude, declares a struct with two four-bit fields and one big-endian `u16`, reads three bytes, changes one value, and writes the result:
use deku::prelude::*;
#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
#[deku(endian = "big")]
struct DekuTest {
#[deku(bits = 4)]
field_a: u8,
#[deku(bits = 4)]
field_b: u8,
field_c: u16,
}
fn main() {
let data: Vec<u8> = vec![0b0110_1001, 0xBE, 0xEF];
let (_rest, mut val) = DekuTest::from_bytes((data.as_ref(), 0)).unwrap();
assert_eq!(DekuTest {
field_a: 0b0110,
field_b: 0b1001,
field_c: 0xBEEF,
}, val);
val.field_c = 0xC0FE;
let data_out = val.to_bytes().unwrap();
assert_eq!(vec![0b0110_1001, 0xC0, 0xFE], data_out);
}A reader following this example should see the input split into values 6 and 9 for the first byte, followed by `0xBEEF`. After `field_c` changes to `0xC0FE`, the serialized first byte remains unchanged and the final two bytes become `0xC0, 0xFE`. The use of `.unwrap()` makes this a concise example, but a production parser would need to define its own error-handling policy.
Deku's no_std and feature constraints
Deku has an explicit no_std configuration. The README disables default features and enables `alloc`:
[dependencies]
deku = { version = "0.20", default-features = false, features = ["alloc"] }The feature graph in `Cargo.toml` provides more detail than the README. The default features are `std`, `bits`, and `descriptive-errors`. The `std` feature propagates to the derive crate, `bitvec`, `alloc`, and `no_std_io/std`. Enabling `bits` makes `bitvec` and the derive crate's bit support available. Enabling `descriptive-errors` also enables allocation.
These flags affect compilation rather than merely formatting diagnostics. A project choosing the no_std dependency line is changing its default feature set, so the Cargo feature graph must agree with the target's standard-library and allocation requirements. Deku's feature list also includes `logging`, which activates derive-side logging and the `log` dependency.
The repository includes `ensure_no_std/` and `ensure_wasm/`, showing that the project has dedicated source trees for those configurations. The README does not state a supported target matrix or enumerate every tested feature combination. Those directories are evidence of repository coverage, not a blanket guarantee for an arbitrary target.
Compiler requirements also require care. The README says rustc 1.81+, while `Cargo.toml` for version 0.20.3 declares `rust-version = "1.88"`. The Cargo metadata is the stricter constraint for that release. A project selecting Deku 0.20.3 should therefore plan for rustc 1.88 unless the release maintainers document a different compatibility boundary.
Enums, custom conversions, and input handling
The repository layout shows that Deku supports more conversion patterns than the main struct example. `examples/enums.rs` and `examples/enums_catch_all.rs` indicate separate attention to standard enum cases and catch-all handling. `examples/custom_reader_and_writer.rs` indicates that developers can examine custom reader and writer paths. Other files include `deku_input.rs`, `read_all.rs`, and `many.rs`.
The README directs users to the documentation and examples for further usage, so it would be speculative to describe the exact attributes or return behavior in those files. This is a meaningful limitation when selecting a serialization crate. Binary formats commonly involve tags, unknown variants, length-delimited collections, or bytes belonging to the next packet, and those cases require more than fixed struct fields.
The `from_bytes()` example returns a `Result`, which shows that decoding failure is represented in the API. Default features include `descriptive-errors`, but the README does not list concrete error types or explain whether errors preserve field position or other context. Projects that match parser errors, recover from unknown tags, or distinguish truncation from malformed values should confirm those details in the API documentation before adopting Deku.
The derive implementation is a separate workspace member named `deku-derive`. `Cargo.toml` refers to it through both a compatible version and a local path, with default features disabled at the dependency boundary. That separation lets the main crate expose generated methods while derive-specific behavior remains in its own package. It also means upgrades should consider the coordinated crate versions.
Deku compared with handwritten binary conversion
The most direct alternative is handwritten reader and writer functions for the same Rust types. Handwritten conversion requires no derive attributes and leaves every input check, branch, and error decision visible in the project. That control can be useful for an unusual format whose validation order or performance-sensitive path is central to the application.
Deku's documented difference is symmetry. The same type declaration supplies both `DekuRead` and `DekuWrite`, while attributes describe details such as bit width and endianness. A handwritten solution must implement and maintain decoding and encoding separately unless the engineer builds an additional abstraction. Deku moves that duplication into generated code, at the cost of requiring proc-macro syntax and accepting behavior defined by Deku's attributes.
The choice is clearest across workload types. Deku is more relevant when several structs or enums have fixed layouts and the team wants one declarative definition for both directions. Handwritten routines may be clearer when a format has only a few fields, conversion logic is unusually dynamic, or existing error and recovery behavior must remain exactly as written.
No benchmark numbers, binary-size comparisons, or generated-code performance measurements appear in the README or repository metadata. This article therefore does not claim that Deku produces faster or smaller output than handwritten code. Evaluation should focus on schema readability, compatibility with the required error model, and the behavior of actual protocol layouts rather than an unsupported performance assumption.
Deku's release, license, and adoption boundaries
Deku is offered under `MIT OR Apache-2.0`. Those identifiers appear in `Cargo.toml`, and the repository contains both `LICENSE-MIT` and `LICENSE-APACHE`. A project must evaluate those terms against its own distribution requirements, but the repository clearly supplies a dual-license choice.
The repository is not archived, and its last push was on 2026-09-24. The listed releases include `deku-v0.20.3` on 2026-01-15, plus `deku-v0.20.2` and `deku-v0.20.1`, both dated 2025-11-25. These facts document release and repository activity. They do not establish API stability, and the `0.20` dependency line should not be interpreted as a promise that future patch releases will be compatible without reading the changelog.
The top-level repository contains `CHANGELOG.md`, so an upgrade can be evaluated against project-provided release notes. It also contains `release-plz.toml`, but that file's contents are not available here, and no release automation behavior should be assumed from its name alone. Because the version is below 1.0 and the library includes a proc-macro package, an adoption review should check both API attributes and compiler requirements.
Deku is a poor fit when the team does not want derive macros on its format types or needs conversion behavior that cannot be expressed through the documented attributes. It is a better fit when the format is fixed, Rust is already the implementation language, and symmetric encode and decode definitions reduce maintenance. The first concrete verification point is the actual target Rust version: `deku` 0.20.3 declares `rust-version = "1.88"`.
Editorial conclusion
Deku is appropriate for Rust projects that define a fixed binary layout and want reading and writing generated from the same field declarations. It is less suitable for teams that require handwritten control over every conversion branch or cannot accept proc-macro attributes in their public types. Before adoption, verify bit order, field widths, enum representation, leftover-input handling, feature selection, and the compiler version required by the release. For `deku` 0.20.3, `Cargo.toml` declares rustc 1.88 even though the README states rustc 1.81+.
Frequently asked questions
What version of Rust does Deku require?
The README states rustc 1.81+, while `Cargo.toml` for version 0.20.3 declares `rust-version = "1.88"`. The stricter Cargo metadata value should be used when planning the compiler for that release.
How do I configure Deku for a no_std project?
The README disables default features and enables `alloc`. This differs from the normal dependency declaration because the selected Cargo features control the standard-library, bit, error, and allocation paths compiled into Deku.
Can Deku serialize fields smaller than one byte?
Yes. The README applies `#[deku(bits = 4)]` to two fields and decodes both four-bit values from the first byte of a three-byte input.
Does Deku support custom readers and writers?
The repository contains `examples/custom_reader_and_writer.rs`, which indicates a custom conversion example. Its contents are not reproduced in the README, so the exact mechanism must be checked in the example and API documentation.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/sharksforarms-deku)Star history chart for your README
[](https://hysenlabs.com/projects/sharksforarms-deku)Was this useful?