Skip to content

Fix transaction closure bounds; replace Kani with no_panic checks - #14

Merged
jadamcrain merged 5 commits into
mainfrom
no-panic-and-api-fixes
Oct 15, 2026
Merged

jadamcrain merged 5 commits into
mainfrom
no-panic-and-api-fixes

Conversation

@jadamcrain

Copy link
Copy Markdown
Member

Fixes #12
Fixes #13

API fixes

Both changes only relax trait bounds, so code that compiled before still compiles. I checked this by building rodbus, dnp3 and batt-mono-repo against this branch (about 90 transaction/at_pos call sites). Each fix includes a regression test that fails to compile against the old signature.

Panic-freedom: Kani → no_panic

The Kani harnesses covered 2 routines on buffers of up to 10 bytes, starting from position 0, which didn't support the README's claim. This PR replaces them with:

  • #[no_panic] on every public routine, behind a CI-only no-panic feature.
  • tests/no_panic.rs, which calls every routine so the linker checks each one. It runs in a no-panic profile (release + overflow-checks) so that integer overflow, which only panics in debug builds, is also caught.
  • tests/no_panic_coverage.rs, which runs in the normal test suite and fails if a routine is missing the attribute or the call.

Checked by injecting faults: an unwrap, an unchecked add, a dropped driver call and a missing attribute were each caught. The README now states what the check does and doesn't guarantee. In particular, it only holds for optimized builds with panic = "unwind".

Also

  • Property tests (tests/roundtrip.rs): every numeric routine's byte layout and round trip, failed operations leave the position and buffer unchanged, and transactions are all-or-nothing.
  • CI workflow (there wasn't one): fmt, clippy and tests on stable; the no_panic check; a build on the MSRV.
  • rust-version = "1.77". That's what first_chunk already required in 0.5.0; 1.76 now gets a clear error.
  • CHANGELOG entry for 0.5.1. The version in Cargo.toml is unchanged, so the release can go in a separate commit.

The closure's cursor argument used an elided lifetime, which made the
buffer lifetime higher-ranked and prevented borrowed slices from
escaping the transaction. Bind it to the cursor's lifetime instead.

Fixes #12
at_pos invokes its closure exactly once, so require FnOnce like the
other transaction methods (see #1).

Fixes #13
The Kani harnesses covered two routines on buffers of up to 10 bytes.
Annotate every public routine with #[no_panic] behind a CI-only
no-panic feature instead, and check it by linking a test that calls
each routine in an optimized build with overflow checks enabled.

A separate test fails if a public routine is missing the attribute or
is not called by the check, since the linker only examines code that
is called.
Check every numeric routine's byte layout and round trip, that failed
reads and writes leave the position and buffer unchanged, and that
transactions are all-or-nothing.
CI runs fmt, clippy, and tests on stable, the no_panic link check, and
a build on the minimum supported Rust version (1.77, required by
slice::first_chunk).
@jadamcrain
jadamcrain merged commit 5e19fbf into main Oct 15, 2026
3 checks passed
@jadamcrain
jadamcrain deleted the no-panic-and-api-fixes branch October 15, 2026 18:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WriteCursor::at_pos should accept FnOnce, not Fn ReadCursor::transaction cannot return data borrowed from the input

1 participant