Expand description
§Interoptopus 🐙
The polyglot bindings generator for your library.
Write a robust library in Rust, easily access it from your second-favorite language:
- Design a single
.dll/.soin Rust, consume it from anywhere. - Get
QoLfeatures (e.g., classes, strings) in languages that have them. - Painless workflow, no external tooling required.
- Easy to support more languages, backends fully decoupled from main project.
We strive to make our generated bindings zero cost. They should be as idiomatic as you could have reasonably written them yourself, but never magic or hiding the interface you actually wanted to expose.
§Code you write …
use interoptopus::{ffi, function};
use interoptopus::inventory::RustInventory;
#[ffi]
pub struct Vec2 {
pub x: f32,
pub y: f32,
}
#[ffi]
pub fn my_function(input: Vec2) {
println!("{}", input.x);
}
// List functions you want to export, types are inferred.
pub fn ffi_inventory() -> RustInventory {
RustInventory::new()
.register(function!(my_function))
.validate()
}§… Interoptopus generates
| Language | Crate | Sample Output1 | Status |
|---|---|---|---|
| C# | interoptopus_csharp | Interop.cs | ✅ |
| C | interoptopus_c | - | ⏯️ |
| Python | interoptopus_cpython | - | ⏯️ |
| Other | Write your own backend2 | - |
✅ Tier 1 target. Active maintenance and production use. Full support of all features.
⏯️ Tier 2 target. Currently suspended, contributors wanted!
1 For
the reference project.
2 Add basic support for a new language in just a few
hours. No pull request needed.
§Getting Started 🍼
If you want to …
- get started see the hello world,
- productify your project, see the real project layout,
- understand what’s possible, see the reference project,
- support a new language, copy the C backend.
§Supported Rust Constructs
See the reference project for an overview:
- functions (freestanding functions and delegates)
- types (composites, enums, opaques, references, …)
- constants ( primitive constants; results of const evaluation)
- patterns (ASCII pointers, options, slices, …)
- services (turn to classes in C# and Python, and async methods)
§Performance 🏁
Generated low-level bindings are zero cost w.r.t. hand-crafted bindings for that language.
That said, even hand-crafted bindings encounter some target-specific overhead at the FFI boundary (e.g., marshalling, pinning, and safety checks). For C# that cost is often nanoseconds, for Python it can be microseconds.
For a quick overview, this table lists some common round trip times in ns / call:
| Construct | C# |
|---|---|
primitive_void() | 3 |
primitive_u64(0) | 4 |
pattern_option(Option.None) | 14 |
pattern_delegate_adhoc(x => x[0]) | 477 1 |
pattern_delegate_retained(delegate) | 21 |
pattern_ascii_pointer("hello world") | 20 |
pattern_utf8_string("hello world") | 52 |
await serviceAsync.Success() | 361 2 |
1 First time delegate creation and pinning is expensive in C# (100’s of ns). We
recommend you retain the delegate instead for >20x faster
calls, see for example here.
2 Preliminary numbers for full round trip to tokio and back. Although async calls have some intrinsic
overhead
(e.g., spawning a new TaskCompletionSource is ~100ns), some of that overhead appears to be a
benchmarking effect when spin-waiting for a newly spawned task. In essence, if your application
benefits from async this overhead is negligible, but simple getters or setters shouldn’t needlessly be made async.
§Feature Flags
Gated behind feature flags, these enable:
derive- Proc macros such as#[ffi].serde- Serde attributes on internal types.log- Invoke log on FFI errors.tokio- Convenience support for async services via Tokio.
§Changelog
- v0.16 - Total rewrite: better architecture,safety, diagnostics.
- v0.15 - Massive cleanup, bugfix, UX overhaul (+syn2).
- v0.14 - Better inventory UX.
- v0.13 - Python backend uses
ctypesnow.
Also see our upgrade instructions.
§FAQ
§Contributing
PRs are very welcome!
- Submit small bug fixes directly. Major changes should be issues first.
- New features or patterns must be materialized in the reference project and accompanied by at least an C# interop test.
Modules§
- ffi
- FFI-safe replacements for common Rust standard library types.
- inventory
- Collection of all FFI items a library wants to expose.
- lang
- Data model describing the items of an FFI boundary.
- pattern
- Convenience patterns like strings, results and options.
- rt
tokio - Optional runtime support for async services.
- wire
- Serialize complex objects into flat byte buffers and transfer them over FFI.
Macros§
- api_
guard - Creates and registers an API guard for the current library.
- builtins_
string - Emits and registers helpers for
ffi::String. - builtins_
vec - Emits and registers helpers for
ffi::Vec<T>types. - builtins_
wire - Emits and registers helper functions used by
Wire. - callback
- Defines a callback type, akin to a
fn f(T) -> Rwrapped in anOption. - constant
- Registers a constant with the inventory.
- extra_
type - Registers an additional type with the inventory.
- function
- Registers a function with the inventory.
- service
- Registers a service with the inventory.
Enums§
- Error
- Can be observed if something goes wrong.
Attribute Macros§
- ffi
derive - Marks a Rust item for FFI export and generates the required interop metadata.
Derive Macros§
- Async
Runtime derive - Derives the
AsyncRuntimetrait for a service struct by forwarding to one of its fields.