# Postcard for Rust: Compact Serde Serialization in no_std Systems

> Postcard is a no_std-focused serializer and deserializer for Serde, aimed at embedded and other constrained Rust environments. Its main trade-off is a compact binary format with documented cautions around unsupported Serde attributes.

**jamesmunns/postcard** — A no_std + serde compatible message library for Rust

- Repository: https://github.com/jamesmunns/postcard
- Stars: 1,541 · Forks: 143
- Language: Rust
- License: Apache-2.0
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/jamesmunns-postcard

## What Postcard solves for constrained Rust

Postcard is a serializer and deserializer for Serde with a primary focus on `#![no_std]` programs. The README identifies embedded systems and other constrained environments as its main target, so the library is aimed at developers choosing a binary message format rather than developers seeking a human-readable configuration format. Its stated goals include minimizing memory usage first, followed by code size, developer time, and CPU time.

The library is designed to keep communication code similar between microcontrollers and desktop or server programs. That matters when a message type is shared across constrained devices and larger systems: the serialization interface can remain based on Serde rather than changing to a device-specific encoding API. Postcard also aims to support a broad set of Serde features, allowing it to serve as a Serde format replacement for suitable message types.

This is a focused library rather than a complete networking stack. The documented example serializes a borrowed byte slice and string, then deserializes the same value from the resulting buffer. It does not define transports, retries, framing, acknowledgements, or application-level message schemas.

## Postcard wire format and serialization mechanics

Postcard uses a compact binary representation. According to the README, signed and unsigned integers larger than eight bits are encoded with a Varint. The same encoding is used for array slice lengths and enum discriminants. This design reduces the number of bytes needed for values whose values are small, although the README does not provide compression information or universal size guarantees.

The format is not presented as a casual implementation detail. The README states that, as of `v1.0.0`, Postcard has a documented and stable wire format, with the specification stored in the repository's `spec/` folder and published at `https://postcard.jamesmunns.com`. That makes the encoding a protocol boundary worth reviewing before deploying multiple devices or services that must interoperate.

The library also has a Flavors system. The README describes flavors as plugins or middleware used during serialization and deserialization. They can be combined to obtain protocol formats with different processing behavior. The repository layout includes separate source crates such as `postcard-core`, `postcard-derive`, `postcard-dyn`, `postcard-schema`, and related packages, but the README does not document the complete configuration surface of every flavor.

## Installing Postcard and completing a first message

The README's setup instructions use the `postcard` version `1.1` and add the `no-std` subset of `serde` by disabling its default features. This configuration is appropriate for a Rust target that cannot depend on the standard library. Add the dependencies to the `[dependencies]` section of the application's `Cargo.toml`.

```toml
[dependencies]
postcard = "1.1"

serde = { version = "1.0", default-features = false }
```

The first example serializes a borrowed message into a fixed-capacity `heapless::Vec<u8>`. The `to_vec` call returns a result, so the application must handle allocation or capacity failure rather than assuming that serialization always succeeds.

```rust
use core::ops::Deref;
use serde::{Serialize, Deserialize};
use postcard::{from_bytes, to_vec};
use heapless::Vec;

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
struct RefStruct<'a> {
    bytes: &'a [u8],
    str_s: &'a str,
}
let message = "hElLo";
let bytes = [0x01, 0x10, 0x02, 0x20];
let output: Vec<u8, 11> = to_vec(&RefStruct {
    bytes: &bytes,
    str_s: message,
}).unwrap();
```

The README then deserializes the buffer with `from_bytes` and compares the result with the original value.

```rust
let out: RefStruct = from_bytes(output.deref()).unwrap();
assert_eq!(
    out,
    RefStruct {
        bytes: &bytes,
        str_s: message,
    }
);
```

The alternative `alloc` feature uses `to_allocvec` and an `alloc::vec::Vec<u8>`. The README includes this as an optional allocation-backed path, while the default `heapless` feature is the more direct example for constrained targets.

## Feature choices and the heapless versus alloc boundary

Postcard offers at least two output paths in the documented examples. `heapless` writes into a fixed-capacity vector, which avoids a conventional heap allocation but requires the caller to choose and enforce a capacity. `alloc` writes into an allocating vector through `to_allocvec`, which is more convenient where an allocator is available but is not equivalent to a no-allocation design.

That distinction affects application code beyond the serializer call. A fixed capacity can produce an error when a message does not fit, while an allocating vector can grow but requires the target to provide `alloc`. The README does not document retry, partial-write, or streaming behavior for the `heapless` example, so applications should not infer those semantics from the example.

The workspace also contains `postcard2` and `postcard2-heapless`, as shown in the top-level `Cargo.toml`. The README focuses on the `postcard = "1.1"` package and does not explain whether adopters should use that package or the later workspace crates for a new design. That package-selection question needs checking against the project's current crate documentation before implementation.

## Serde attributes that can break a message format

Postcard's Serde integration has explicit hazards, and the README does not treat attributes as universally interchangeable. It recommends extensive testing when `serde` attributes are used because several attributes can cause serialization or deserialization problems.

The README specifically lists `serde(flatten)` and `serde(skip_serializing_if($COND))` as attributes that break serialization or deserialization in some cases. It also warns about `serde(skip)`. For enum variants, skipped fields must be the last variants; otherwise Postcard may deserialize against an offset discriminant, causing a failure or a silent mismatch on C-like enums. This is a protocol correctness issue, not merely a style concern, because a value can be decoded into the wrong shape without an immediate error.

A practical schema review should therefore identify every Serde attribute used by the message types, then confirm round trips with both serialization and deserialization. The README does not provide a complete compatibility matrix for all Serde attributes, so its tracking issue for this behavior is the relevant place to investigate before relying on an attribute-heavy schema.

## Where Postcard is the wrong format

Postcard is a poor fit when a human-readable or directly editable representation is required, because its documented examples produce binary bytes and the README describes Varint encoding rather than a text syntax. It is also a poor fit when the protocol must be consumed by systems that do not implement the Postcard specification.

The compact binary format can make debugging less direct. The README provides a fixed byte assertion, which is useful for tests, but it does not document a general inspection command, logging format, or schema browser. Engineers should not assume that `from_bytes` failure always identifies the offending field. The README gives no such diagnostic guarantee.

A conventional Serde format with a self-describing textual representation may be easier for command-line tools, manual inspection, or interoperability with systems optimized for JSON or another established encoding. Postcard is more relevant when both ends share Rust types and the design priority is constrained execution. Flavors can alter processing, but the README does not establish that they eliminate interoperability or inspectability concerns.

## Alternatives, licensing, and maintenance

A real alternative is a conventional Serde text format such as a JSON-oriented Serde implementation. The difference is approach: a text format prioritizes readable, externally inspectable data, while Postcard uses a compact binary format and Varints, with the README explicitly prioritizing constrained-resource use. The alternative may require more bytes and may have different parsing costs, but it can be easier to inspect outside Rust.

Postcard is licensed under either the Apache License, Version 2.0 or the MIT license, at the user's option. The repository includes `LICENSE-APACHE` and `LICENSE-MIT`. Contributions are described as dual licensed under the same terms unless contributors state otherwise. That is a licensing choice to account for in an adoption review, not a technical recommendation from this article.

The repository is not archived. Its last push was on 2026-09-21, which is within the six-month period used for maintenance classification. The release list includes `postcard2/v0.2.0` from 2025-12-27, `postcard-schema/v0.2.5` from 2025-08-23, and `postcard-schema-ng/v0.3.2` from 2025-08-23. The README does not provide upgrade procedures or a compatibility policy for moving between these packages, so version selection should be checked against the exact crate being adopted.

## Conclusion

Adopt Postcard when an embedded or otherwise constrained Rust system needs Serde serialization with a documented stable format and `no_std` support. Do not choose it without checking whether your data model uses `serde(flatten)`, `skip_serializing_if`, or unusual `skip` placement, because the README identifies failure and silent-mismatch risks. Verify feature selection, target compatibility, and the exact behavior of every Serde attribute before committing a protocol to it.

## FAQ

### How do I install Postcard for a Rust no_std project?

Add `postcard = "1.1"` and configure Serde with `default-features = false` in the project's `[dependencies]` section. The README specifically warns that Serde's default `std` feature is unsuitable for embedded targets.

### Does Postcard allocate memory when serializing?

The README documents a default `heapless` example using `heapless::Vec<u8>` with a fixed capacity, and an optional `alloc` example using `to_allocvec` with `alloc::vec::Vec<u8>`. The available output path depends on the selected feature and target environment.

### Which Serde attributes should I avoid with Postcard?

The README calls out `serde(flatten)` and `serde(skip_serializing_if($COND))` as attributes that can break serialization or deserialization. It also warns that `serde(skip)` can cause enum deserialization failures or silent mismatches when skipped fields are not the last variants.

## Sources

- [Issues](https://github.com/jamesmunns/postcard/issues)
- [jamesmunns/postcard on GitHub](https://github.com/jamesmunns/postcard)
- [License: Apache-2.0](https://github.com/jamesmunns/postcard/blob/main/LICENSE)
- [README](https://github.com/jamesmunns/postcard/blob/main/README.md)
- [Releases](https://github.com/jamesmunns/postcard/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/jamesmunns-postcard
