Cross-Crate
Probably the feature I like the most about ezffi.
This chapter will cover the scenario where crate B depends on crate A, both use ezffi, and B wants a function that takes a type A exports, without re-declaring A's types or duplicating the C definitions.
TypeMap, the logic that makes this possible
The ezffi macro uses a shared registry that lives at target/ezffi-typemap.toml (one per workspace, shared across all profiles and across cbindgen parse-expand sub-builds). Each crate writes its own section when its macros expand, and reads every other crate's section when resolving foreign types. The ezffi-typemap.toml looks like:
[dependency_crate] # Crate A
codegen_version = 1
BigInteger = { ffi = "DependencyCrateBigInteger", c_compatible = false }
[your_crate] # Crate B
codegen_version = 1
User = { ffi = "YourCrateUser", c_compatible = false }
Color = { ffi = "YourCrateColor", c_compatible = true }
When crate B writes &dependency_crate::BigInteger in an exported signature, the macro looks up BigInteger in the typemap, finds it lives in dependency_crate, and emits *const dependency_crate::DependencyCrateBigInteger on the C side. B should update their cbindgen.toml includes field to add dependency_crate/dependency_crate.h, and the type is reused.
The file gets a per-section lock so concurrent macro expansions across the workspace don't stomp on each other.
Codegen failures at compile time
Every typemap section records the codegen version that produced it. Currently we are on codegen version 1, and I hope I don't have to change it. When you upgrade ezffi and one of your dependencies is still on an older codegen, the macro refuses to read its section and won't compile. The error tells you which crate is out of sync and which ezffi version fixes it, copy-pasteable into your Cargo.toml. If ezffi changes the codegen version, a new major release will happen.
Making your library compatible with the ezffi cross-crate feature
There's nothing extra to opt into, using #[ezffi::export] already registers your types in the typemap. To make consumers' lives easier, and your crate predictable, I'd like to define a set of rules:
- Optionally hide the
ezfficompatibility behind anezffifeature, this will help your library performance for Rust consumers, see Benchmarks for more info. - If you hide
ezffibehind a feature remember to addfeatures = ["ezffi"]to yourcbindgen.toml - Re-export the original Rust-types at your crate root (
pub use structs::*;). The downstream crate then writesuse dep::User; … &User …instead of digging into module paths.ezffiwill also try to usedep::Userto resolve the type. - Make sure your generated header lives at
target/<profile>/include/<your_crate>/<your_crate>.h(the recommendedbuild.rsfrom Getting Started already does this). That's the path the consumer'scbindgen.tomlwill reference in itsincludeslist. Feel free to split your exported symbols into different headers but always under your crate's folder, the same wayezffiheaders are generated. - Pin a codegen-compatible
ezffirange in yourCargo.toml. Consumers will pick a range that overlaps with yours; if you start emitting a new codegen version, mention it in your CHANGELOG. It's also recommended to mention the codegen yourezffiversion is emitting in the section where you talk aboutezffisupport in your README, book, etc.