An experimental alternative to Rust sans-io that enables the direct style
README.md

include/ #

effects.h is the C ABI of ../rust/cdylib, as a header. It is generated — cbindgen reads the crate's #[no_mangle] pub extern "C" functions and their doc comments and writes this file — and it is checked in.

Why generated #

The prototypes used to be typed by hand in each host (cgo's preamble, Panama's FunctionDescriptors). That is a second copy of the ABI that nothing checks against the first. cbindgen makes the header from the Rust, so a changed signature changes the header, and every host that compiles against it notices. ../ABI.md describes the contract in prose; this file is the part of it a compiler can hold you to.

Why checked in #

Two hosts consume it at their own compile time, not at ours:

consumer how
../go/cgo #include "effects.h" in the cgo preamble; cgo generates Go bindings from it
../java/jextract jextract effects.h generates effects_h.java, one static method per export

If the header lived in target/, every host build would first need cargo and cbindgen on the path and a build step to run. Checked in, go run . and jextract just work, and the header is readable in the tree — it is the shortest complete statement of what the cdylib exports.

The cost is a hand-sync obligation: after changing an export, run

../rust/cdylib/gen-header.sh

and commit the diff. A CI check that the committed header matches a fresh generation is the missing piece (see TODO). Do not edit the header directly; edit the Rust.

Why include/ #

C convention: the directory you pass to -I. Nothing more.