Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

FAQ

Why not UniFFI?

UniFFI is excellent and ships in production at Mozilla; if you need only Swift, Kotlin, and Python and want maximum maturity, it’s the safer pick. WeaveFFI exists for a different set of needs: eleven first-party targets from one definition, a producer that can be written in any language with a C ABI (the generated header is a public contract, not private scaffolding), a standalone CLI built for CI, and a schema-checked YAML, JSON, or TOML IDL. UniFFI is still ahead on async callback methods, custom types, and cross-crate type imports. See the comparison for the full table.

Can I use it with a C++ codebase?

To consume a library from C++, --target cpp emits a header-only RAII API with std::optional, std::vector, std::future, exceptions, and a CMakeLists.txt; see C++. To expose an existing C++ library, describe the surface in an IDL and implement the generated C header in C++; WeaveFFI doesn’t parse C++ headers. For automatic wrapping of C++ headers, look at autocxx or SWIG.

Does it support generics?

Through built-in shapes rather than user-defined generics: T?, [T], {K:V}, and iter<T>, which compose with each other and with every other type, including objects ([Store], {string:Store}, iter<Store>). Arbitrary generics would push type erasure into every generator; trait-object interfaces are on the roadmap.

What’s the runtime overhead?

A call costs the marshalling of its arguments, one extern "C" call, and the marshalling of its result. Scalars pass by value. Strings and bytes pass as borrowed (ptr, len) views, so the producer copies only what it keeps. Objects pass as one pointer. Records and collections are encoded into one value buffer per value, with single-copy fast paths for byte and numeric lists. Async calls add a completion callback and whatever executor drives the future. The runtime itself is the small weaveffi::abi module; the leak-check counters cost an atomic update per counted operation and are off by default.

How are errors propagated?

Every fallible C call takes a {prefix}_error* out-parameter; async calls receive one in their completion. Positive codes are your declared error domain, negative codes are the runtime’s (generic, panic, marshalling, callback failure, cancelled). A function marked throws: true (a Result in Rust) surfaces domain codes as typed errors in every language; a function without it traps on failure, since a failure there is a bug. See Errors and Memory.

Can two WeaveFFI libraries live in one process?

Yes. Every symbol, type, macro, package, and environment variable is named after the library’s own prefix and name (kvstore_error, KVSTORE_LIBRARY), and none after WeaveFFI, so two libraries never collide, whether linked statically or loaded dynamically. Each carries its own copy of the small runtime.

What happens if the bindings and the library drift apart?

They refuse to load. Every generated consumer compares the library’s ABI revision with the one it was generated against, and checks that every declaration it uses is in the library’s contract table with the same signature hash. On a mismatch it raises the language’s load error naming the declaration that’s missing or changed. The hashes cover names, types, and flags, but not documentation or declaration order, so editing doc comments never breaks a deployed binding, and neither does adding a function, method, or type. In CI, weaveffi diff --check catches stale committed bindings before they ship.

Does the CLI parse my Rust source?

No. #[weaveffi::module] embeds a description of the API in the library it compiles, and weaveffi generate builds the crate and reads that description back out of the library, so macros, type aliases, and #[cfg] are already resolved and the bindings match the build exactly. weaveffi extract prints it as an IDL. See Library Mode.

Can I customize the generated code?

Through weaveffi.toml: package metadata, per-target names (the Swift module, the Kotlin package, the Go module path, each set with name), and target-specific options. Run formatters as a separate step after weaveffi generate. See Project Configuration. Changing the C ABI itself is a generator contribution; see the architecture guide.

Does it work with Flutter?

The Dart target emits dart:ffi bindings with a pubspec.yaml usable from Flutter and plain Dart on every platform that supports dart:ffi. weaveffi package --target dart bundles the desktop libraries under native/<platform>/; for iOS and Android you add the library to the Flutter app’s native build yourself, since Flutter’s native-assets build isn’t generated yet. For the web, use the WebAssembly target. See Dart.

Is it Windows-friendly?

The CLI is plain Rust and runs on Windows, and the workspace’s tests run on Windows in CI. Headers carry a {PREFIX}_API macro that resolves to __declspec(dllimport) for consumers and dllexport when a C or C++ producer defines {PREFIX}_BUILD, and every loader knows the {library}.dll naming. The conformance harness runs on Linux and macOS today; Windows lanes are on the roadmap.

How do I distribute the native library?

Build it per platform and let weaveffi package bundle it into each ecosystem’s package layout (npm platform packages, platform wheels, NuGet runtimes/, Android jniLibs/, and so on), then publish with each ecosystem’s own tools. Every generated manifest takes its name and version from [package]. See Packaging.

Who owns an object, and when is it freed?

The producer reference-counts it. Each wrapper holds one strong reference and releases it through the language’s disposal idiom (close(), Dispose(), deinit, a destructor), with a garbage-collector backstop; the object is freed when the last reference anywhere goes. Objects returned, yielded, delivered, or passed to a callback are references the receiver adopts; objects passed to the producer are borrowed. See Errors and Memory.

Which executor runs my async functions, and how does cancellation work?

By default, a small pool of worker threads (one per core, at least two) started on the first call. With the weaveffi crate’s tokio feature it’s Tokio: the current runtime when the call is made from inside one, otherwise one the library creates. weaveffi::set_spawner installs any other executor and overrides both. Consumers see their native async idiom, and cancelling it (a Swift Task, a Kotlin coroutine, an AbortSignal, a CancellationToken, a Go context) cancels the native call: the runtime drops the future and completes with the cancelled code, which surfaces as the language’s cancellation error. See Async and Cancellation.

What’s the license?

MIT OR Apache-2.0, at your option. Generated code carries no license header of its own and is yours to license as you like.