proofs.rs

The heapless feature does not select allocation-free serialization

The heapless feature does not select allocation-free serialization

Found in Report #9 v1 · Updated 2026-10-28 13:51 UTC

Description

With heapless enabled, human-readable serialization should use an allocation-free buffer; the serializer itself may still allocate. Instead, the following implementations can allocate while formatting:

  • Serialize for Ipv4Net
  • Serialize for Ipv6Net
  • Serialize for IpNet, which delegates to those implementations

The feature definition describes heapless as avoiding dynamic allocations during serialization. In the serialization implementation, the buffer path is selected by ser_as_str, while heapless alone uses collect_str.

Steps to Reproduce

Create a project with these files:

Cargo.toml
[package]
name = "ipnet-heapless-repro"
version = "0.1.0"
edition = "2021"
[workspace]
[features]
heapless = ["ipnet/heapless"]
legacy = ["ipnet/serde", "ipnet/ser_as_str"]
[dependencies]
ipnet = "=2.12.2"
serde = "=1.0.229"
serde_test = "=1.0.177"
allocation-counter = "=0.8.1"
heapless = "=0.9.2"
src/main.rs
use serde_test::{assert_ser_tokens, Configure, Token};

fn main() {
    let net: ipnet::Ipv4Net = "255.255.255.255/32".parse().unwrap();
    let info = allocation_counter::measure(|| {
        assert_ser_tokens(&net.readable(), &[Token::Str("255.255.255.255/32")]);
    });
    assert_eq!(info.count_total, 0);
}

Run each command separately:

cargo +1.91.1 run --features heapless
cargo +1.91.1 run --features legacy

serde_test checks the exact string and its serialize_str does not allocate. The example measures serialization after parsing; the assertion expects zero allocations. legacy enables serde and ser_as_str as a control.

Expected Behavior

Both commands succeed because formatting uses an allocation-free buffer with this serializer.

Actual Behavior

The heapless command fails the assertion (three allocations in the observed run); the legacy command succeeds. Allocation counts can vary with the environment.

Environment

ipnet 2.12.2, serde 1.0.229, heapless 0.9.2, Rust 1.91.1, macOS arm64.

Change history
  1. v1 · created · 2026-10-28 13:36 UTC
    {
      "title": "The heapless feature does not select allocation-free serialization",
      "body": "## Description\n\nWith `heapless` enabled, human-readable serialization should use an allocation-free buffer; the serializer itself may still allocate. Instead, the following implementations can allocate while formatting:\n\n- `Serialize` for `Ipv4Net`\n- `Serialize` for `Ipv6Net`\n- `Serialize` for `IpNet`, which delegates to those implementations\n\nThe [feature definition](https://github.com/krisprice/ipnet/blob/13c5c5873c02127679b1e8e64b538e4e36942640/Cargo.toml#L18) describes `heapless` as avoiding dynamic allocations during serialization. In the [serialization implementation](https://github.com/krisprice/ipnet/blob/13c5c5873c02127679b1e8e64b538e4e36942640/src/ipnet_serde.rs), the buffer path is selected by `ser_as_str`, while `heapless` alone uses `collect_str`.\n\n## Steps to Reproduce\n\nCreate a project with these files:\n\n### `Cargo.toml`\n\n```toml\n[package]\nname = \"ipnet-heapless-repro\"\nversion = \"0.1.0\"\nedition = \"2021\"\n[workspace]\n[features]\nheapless = [\"ipnet/heapless\"]\nlegacy = [\"ipnet/serde\", \"ipnet/ser_as_str\"]\n[dependencies]\nipnet = \"=2.12.2\"\nserde = \"=1.0.229\"\nserde_test = \"=1.0.177\"\nallocation-counter = \"=0.8.1\"\nheapless = \"=0.9.2\"\n```\n\n### `src/main.rs`\n\n```rust\nuse serde_test::{assert_ser_tokens, Configure, Token};\n\nfn main() {\n    let net: ipnet::Ipv4Net = \"255.255.255.255/32\".parse().unwrap();\n    let info = allocation_counter::measure(|| {\n        assert_ser_tokens(&net.readable(), &[Token::Str(\"255.255.255.255/32\")]);\n    });\n    assert_eq!(info.count_total, 0);\n}\n```\n\nRun each command separately:\n\n```sh\ncargo +1.91.1 run --features heapless\ncargo +1.91.1 run --features legacy\n```\n\n`serde_test` checks the exact string and its `serialize_str` does not allocate. The example measures serialization after parsing; the assertion expects zero allocations. `legacy` enables `serde` and `ser_as_str` as a control.\n\n## Expected Behavior\n\nBoth commands succeed because formatting uses an allocation-free buffer with this serializer.\n\n## Actual Behavior\n\nThe `heapless` command fails the assertion (three allocations in the observed run); the `legacy` command succeeds. Allocation counts can vary with the environment.\n\n## Environment\n\nipnet 2.12.2, serde 1.0.229, heapless 0.9.2, Rust 1.91.1, macOS arm64.",
      "validity": "reported",
      "upstream_status": "not_reported"
    }
  2. v2 · link_added · 2026-10-28 13:51 UTC
    {
      "id": "dfd0e29a-c3f9-4af9-8668-dfcfe98f6288",
      "kind": "issue",
      "url": "https://github.com/krisprice/ipnet/issues/73",
      "version": null,
      "target_report_id": null,
      "target_revision_no": null,
      "edit_version": 1
    }

Read and join the discussion on the report →