1 unstable release
Uses new Rust 2024
| 0.1.0 | Sep 29, 2025 |
|---|
#345 in Rust patterns
390KB
9K
SLoC
miniffi
[!WARNING] This project is on its first release. I'm still trying out the idea. It has not been used in production.
This is a simple but opinionated FFI system for Rust. It allows you to call Rust code from other languages, and vice versa. Some design principles:
- Intended for writing platform-agnostic Rust with platform-specific details in the host language
- FFI support is split into data (structs passed by copy) and code (traits passed by reference)
- Rather than have a separate schema language, the public API in your
lib.rsis your schema - The generated binding code is compact, dependency-free, and straightforward to integrate with
Supported Features
- Primitive types (
bool,i32,f64,String, etc.) - Tuples
- Structs
- Enums
- Top-level constants
- Top-level functions
- Traits (must be either
Box<dyn T>orRc<dyn T>) Vec<T>Option<T>Box<T>
Available Targets
- JavaScript/TypeScript (WASM)
- Swift
- C++
Example
After setting everything up, using miniffi looks something like this. The
following example Rust code (in lib.rs) demonstrates passing code and
data back and forth between Rust and the host language:
pub enum Level {
Warning,
Error,
}
pub trait Logger {
fn log(&self, level: Level, text: &str);
}
pub trait Demo {
fn run(&self, logger: Box<dyn Logger>);
}
pub fn get_demo() -> Box<dyn Demo> {
struct DemoImpl;
impl Demo for DemoImpl {
fn run(&self, logger: Box<dyn Logger>) {
logger.log(Level::Warning, "example");
}
}
Box::new(DemoImpl)
}
// This includes the binding code generated by miniffi
include!(concat!(env!("OUT_DIR"), "/miniffi.rs"));
Unlike other FFI systems, there are no annotations required to expose something
from lib.rs to the host language other than using pub to mark things public.
The miniffi build script parses lib.rs, extracts the supported parts of the
public API, and generates FFI binding code both for Rust (the miniffi.rs file
referenced above) and for the host language. Calling that example Rust code
might look like this:
In JavaScript/TypeScript:
import * as rust from "./miniffi.js"
import fs from "fs"
await rust.instantiate(fs.readFileSync(
"./target/wasm32-unknown-unknown/debug/example.wasm"))
rust.get_demo().run({
log(level, text) {
if (level === rust.Level.Error)
console.error("ERROR:", text)
else if (level === rust.Level.Warning)
console.warn("WARNING:", text)
}
})
In Swift:
class LoggerImpl : Logger {
func log(_ level: Level, _ text: String) {
if level == .Error {
print("ERROR:", text)
} else if level == .Warning {
print("WARNING:", text)
}
}
}
get_demo().run(LoggerImpl())
In C++:
#include <iostream>
#include "ffi.h"
struct LoggerImpl : rust::Logger {
void log(rust::Level level, std::string text) {
if (level == rust::Level::Error)
std::cout << "ERROR: " << text << std::endl;
else if (level == rust::Level::Warning)
std::cout << "WARNING: " << text << std::endl;
}
};
int main() {
rust::get_demo()->run(std::make_unique<LoggerImpl>());
return 0;
}
More information about how to use miniffi can be found in the documentation.
Similar Projects
These projects are much further along, and you may want to use them instead:
Dependencies
~160–570KB
~14K SLoC