Comparison
WeaveFFI sits in a crowded ecosystem of FFI tooling. This page is an honest, side-by-side look at how it compares to the projects you are most likely to evaluate against it: UniFFI, cbindgen, diplomat, SWIG, and autocxx.
All comparisons reflect the public state of each project at the time of writing. If something here is out of date, please open a PR.
At a glance
| WeaveFFI | UniFFI | cbindgen | diplomat | SWIG | autocxx | |
|---|---|---|---|---|---|---|
| Source language | Rust / C / C++ / Zig (anything with a C ABI) | Rust | Rust | Rust | C / C++ | C++ |
| Input format | YAML / JSON / TOML IDL | UDL or proc-macro on Rust | Rust source (annotated) | Rust source (annotated) | C/C++ headers + .i interface | C++ headers |
| Languages | ||||||
| C | ✓ | — | ✓ | ✓ | ✓ | — |
| C++ | ✓ (RAII, std::optional/vector/unordered_map) | — | ✓ (header) | ✓ | ✓ | ✓ (its purpose) |
| Swift | ✓ (SwiftPM, async/await, throws) | ✓ | — | ✓ | — | — |
| Kotlin / Android (JNI) | ✓ (Kotlin + JNI shim + Gradle) | ✓ | — | — | ✓ (Java via JNI) | — |
| Node.js | ✓ (N-API + .d.ts) | community add-on | — | — | ✓ (JavaScriptCore/V8) | — |
| WebAssembly | ✓ (loader + .d.ts) | — | — | ✓ (JS via WASM) | — | — |
| Python | ✓ (ctypes + .pyi) | ✓ | — | — | ✓ | — |
| .NET / C# | ✓ (P/Invoke + .csproj) | ✓ (community) | — | — | ✓ | — |
| Dart / Flutter | ✓ (dart:ffi) | community | — | ✓ | — | — |
| Go | ✓ (CGo) | community | — | — | ✓ | — |
| Ruby | ✓ (FFI gem) | — | — | — | ✓ | — |
| Type system | ||||||
Primitives + string | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
bytes / byte slices | ✓ | ✓ | ✓ (raw) | ✓ | partial | ✓ |
| Structs | ✓ (opaque + getters) | ✓ (records & objects) | ✓ (#[repr(C)]) | ✓ (opaque) | ✓ | ✓ |
| Enums w/ explicit discriminants | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Optionals | ✓ (T?) | ✓ | partial | ✓ | partial | ✓ |
| Lists | ✓ ([T]) | ✓ | partial | ✓ | ✓ | ✓ |
| Maps | ✓ ({K:V}) | ✓ | — | ✓ | partial | partial |
Typed handles (handle<T>) | ✓ | ✓ (objects) | — | ✓ (opaque) | partial | — |
Borrowed types (&str, &[u8]) | ✓ | partial | ✓ | ✓ | — | ✓ |
Iterators (iter<T>) | ✓ | ✓ (callbacks) | — | partial | partial | — |
| Async functions | ✓ (callback ABI + async/await/Promise/suspend/Task<T>) | ✓ | — | partial | — | — |
| Cancellable futures | ✓ (weaveffi_cancel_token) | partial | — | — | — | — |
| Callbacks / event listeners | ✓ (module-level) | ✓ | — (raw fn ptrs) | partial | partial | partial |
| Cross-module type references | ✓ | ✓ | n/a | ✓ | ✓ | ✓ |
| Nested modules | ✓ | partial | n/a | ✓ | ✓ | ✓ |
| Workflow | ||||||
| Single-binary CLI install | ✓ (cargo install weaveffi-cli) | ✓ | ✓ | ✓ | system package | ✓ |
| Standalone publishable packages | ✓ (npm, SwiftPM, pub.dev, NuGet, gem, etc.) | partial | n/a | partial | partial | n/a |
| JSON Schema for IDL editor support | ✓ | — | n/a | n/a | — | n/a |
extract from annotated source | ✓ (Rust) | ✓ (proc-macro) | ✓ (Rust) | ✓ (Rust) | n/a | ✓ (C++) |
watch mode | ✓ | — | ✓ (--watch) | — | — | partial |
format IDL canonicalizer | ✓ | — | n/a | n/a | — | n/a |
Schema migrations (upgrade) | ✓ | — | n/a | n/a | — | n/a |
| Custom template overrides | — | partial (Mako) | — | partial | ✓ (%typemap) | partial |
| Snapshot-tested generator output | ✓ | ✓ | ✓ | ✓ | partial | ✓ |
| Maturity | pre-1.0 | 1.0+ in Mozilla shipping products | 1.0+ widely deployed | pre-1.0 | 30+ years, ubiquitous | pre-1.0 |
| License | MIT OR Apache-2.0 | MPL-2.0 | MPL-2.0 | BSD-3-Clause | GPL with FOSS exception | MIT OR Apache-2.0 |
Legend: ✓ = first-class support; partial = supported with caveats or via extensions; — = not supported; n/a = not applicable to that tool’s scope.
Where competitors are stronger
We try hard to be honest about the trade-offs. Pick the right tool for the job:
- UniFFI is more mature. It ships in production at Mozilla (Firefox Sync, Glean, Nimbus) and has years of battle-testing across iOS, Android, and desktop. If you only need Swift, Kotlin, and Python today and you are comfortable with a UDL-or-proc-macro workflow, UniFFI is the safer choice.
- cbindgen is simpler if all you want is a C header. WeaveFFI generates a C header and ten other targets — if you only consume the C surface from C/C++ code, cbindgen has less ceremony, no IDL file, and a smaller dependency footprint.
- diplomat has a more polished C++ story. Its C++ output uses richer
templates and integrates more cleanly with existing C++ codebases. WeaveFFI’s
C++ output is RAII-based and includes a
CMakeLists.txt, but it’s optimized for greenfield projects, not for slotting into a 20-year-old C++ build system. - SWIG covers languages WeaveFFI doesn’t. Lua, Tcl, R, Octave, Perl, PHP — if your target is exotic, SWIG probably has a generator. SWIG also natively understands C and C++ headers, so you don’t need to author an IDL at all.
- autocxx is unmatched for “wrap an existing C++ library.” It reads your C++ headers directly and uses bindgen + cxx under the hood. WeaveFFI does not parse C++; you describe the surface area you want to expose, and WeaveFFI generates the contract.
- No IDE plugin yet. The other tools listed have community VSCode/JetBrains
extensions of varying quality. WeaveFFI ships a JSON Schema for editor
autocompletion and a
formatcommand, but no first-party IDE plugin. - No formal stability guarantee yet. WeaveFFI is pre-1.0; the IDL,
generated output, and runtime symbol names can shift in minor releases
(always with a
weaveffi upgradepath). UniFFI, cbindgen, and SWIG offer stronger compatibility commitments today.
When to choose WeaveFFI
WeaveFFI is the right pick when you want:
- One source of truth for many languages. If your library has to land in npm and SwiftPM and PyPI and NuGet and pub.dev and RubyGems and a Go module and a Gradle artifact — that’s the WeaveFFI sweet spot. UniFFI covers a smaller subset out of the box; cbindgen and autocxx don’t try.
- Standalone, publishable consumer packages. Generated packages are
self-contained: a Swift consumer adds your
.xcframework+ a SwiftPM manifest and is done. No “install WeaveFFI” step on the consumer side. - A native library that isn’t (only) Rust. WeaveFFI works against
anything that exposes a stable C ABI — Rust (with
--scaffoldconvenience), C, C++, Zig, etc. UniFFI and diplomat assume Rust; autocxx assumes C++. - Idiomatic per-target output, not a lowest-common-denominator API.
Async functions become
async/awaitin Swift,Promises in Node,suspend funin Kotlin,async defin Python, andTask<T>in C# — all from the sameasync: trueflag in the IDL. - A CLI workflow with
validate,lint,diff,watch,format, andupgrade. WeaveFFI is built for monorepos and CI: every sub-command has a--format jsonoutput mode, anddiff --checkandformat --checkare designed to drop into pre-commit and CI gates. - Honest pre-1.0 churn that’s mechanically migratable. Every breaking
IDL change ships with a
weaveffi upgrademigration. You don’t get stuck on an old version because the migration path is missing.
When to choose something else
- You only need Swift + Kotlin + Python and want maximum stability — use UniFFI.
- You only need a C header for a Rust crate — use cbindgen.
- You’re wrapping a large existing C++ codebase — use autocxx (or cxx + bindgen directly).
- Your target language is Lua, Tcl, R, Octave, Perl, or PHP — use SWIG.
- You need a battle-tested C++ binding generator with rich template support — use diplomat or SWIG.
Migrating to / from WeaveFFI
WeaveFFI’s IDL is intentionally close to UniFFI’s UDL surface area, which
makes hand-porting straightforward in either direction. There is no
automatic UDL → WeaveFFI converter today, but weaveffi extract can read
annotated Rust source and produce a starting IDL, which is often the
fastest path off any Rust-only generator. See the
extract guide for details.