Use this page with an LLM
Getting Started
This guide walks you through creating a Rust library and packaging it for use in other languages.
Prerequisites
- Rust 1.70+ (rustup.rs)
- For Apple: Xcode 15+ with command line tools
- For Android: Android Studio with NDK
- For Java: JDK 8+ with
javacand a C compiler for the JNI bridge - For C#: .NET SDK 10.0+ for the current C# demo and generated binding tests
- For WASM: Node.js 18+ and wasm-pack (
cargo install wasm-pack) - For Python, Python 3.10+ with pip
- For C, a C11 compiler such as GCC, Clang, or MSVC
Install BoltFFI
cargo install boltffi_cli
Create a Rust library
cargo new --lib mylib
cd mylib
Edit Cargo.toml:
[package]
name = "mylib"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["staticlib", "cdylib"]
Install BoltFFI:
cargo add boltffi
Write your code
Two attributes do most of the work:
#[data]marks a struct or enum as a record (value type, copied across the boundary)#[export]exposes a function, or a class through its struct and impl blocks, to the target language
Replace src/lib.rs:
use boltffi::*;
boltffi::scaffolding!();
#[data]
#[derive(Clone, Copy)]
pub struct Point {
pub x: f64,
pub y: f64,
}
#[export]
pub fn distance(a: Point, b: Point) -> f64 {
((b.x - a.x).powi(2) + (b.y - a.y).powi(2)).sqrt()
}
boltffi::scaffolding!() goes once in src/lib.rs of every crate that uses BoltFFI. Without
it, every annotated item fails to build with cannot find type `__BoltffiTag` in the crate root.
A signature can name a declared type by its own name, a path, a use rename or a type alias.
After type Coordinate = Point;, a signature taking Coordinate works once boltffi generate,
boltffi build or boltffi pack has run for the crate. A plain cargo build before that fails
with cannot find macro `Coordinate` . Each declared name may appear once per crate, even in
different modules.
Each distinct type an item names adds two levels of macro expansion, so an impl naming 64 or
more declared types reaches rustc’s default recursion limit. Raise it at the crate root with
#![recursion_limit = "256"], as the error suggests.
Types from other crates
A signature can use a #[data], #[error], class or callback declared in a dependency, by
any path that names it. Each such dependency calls boltffi::scaffolding!() too.
- A path dependency in your workspace is bound in full: all of its exports appear in the bindings.
- A crates.io or git dependency contributes only the types your signatures reach.
- Two crates declaring the same name are refused, since the bindings put every name in one namespace.
A dependency’s exported functions are compiled into that dependency, and the linker keeps
them only when your crate uses the dependency. If a path dependency is bound but never used,
boltffi generate fails and lists the missing symbols. Name the crate once in src/lib.rs:
use shapes as _;
custom_type! in a dependency is not supported yet.
Build, package, or generate
boltffi init # creates boltffi.toml
boltffi pack all --release # packages all enabled packaged targets
This creates:
dist/apple/- XCFramework + Swift bindingsdist/android/- JNI libraries + Kotlin bindingsdist/java/- JNI library + Java bindingsdist/csharp/- NuGet package + native runtime assetsdist/wasm/- WASM module + TypeScript bindingsdist/python/- Python source package + wheels
To build for a single platform:
boltffi pack apple
boltffi pack android
boltffi pack java
boltffi pack csharp
boltffi pack wasm
boltffi pack python
Use in Swift
Add the package to Xcode (File → Add Package Dependencies → local path to dist/apple), then:
import MyLib
let p1 = Point(x: 0, y: 0)
let p2 = Point(x: 3, y: 4)
print(distance(a: p1, b: p2)) // 5.0
Use in Kotlin
Copy dist/android into your Android project, then:
import com.example.mylib.*
val p1 = Point(0.0, 0.0)
val p2 = Point(3.0, 4.0)
println(distance(p1, p2)) // 5.0
Use in Java
Add the generated sources and JNI library to your project, then:
import com.example.mylib.*;
Point p1 = new Point(0.0, 0.0);
Point p2 = new Point(3.0, 4.0);
System.out.println(distance(p1, p2)); // 5.0
On Java 16+ with min_version = 16 in boltffi.toml, records are used instead of classes. On Java 8+, the same API works with generated final classes.
Use in C#
Build the NuGet package and add it to your .NET project from a local package source:
boltffi pack csharp
This produces a .nupkg under dist/csharp/packages/ with the bindings and the native runtime
assets bundled under runtimes/<rid>/native/. Reference it from your project just like any other
NuGet dependency, then:
using MyLib;
var p1 = new Point(0.0, 0.0);
var p2 = new Point(3.0, 4.0);
Console.WriteLine(MyLib.Distance(p1, p2)); // 5.0
Use in TypeScript
The WASM package works in bundlers, Node.js, and browsers.
With a Bundler (Vite, webpack)
Import directly - your bundler handles WASM loading:
import { Point, distance } from 'mylib';
const p1: Point = { x: 0, y: 0 };
const p2: Point = { x: 3, y: 4 };
console.log(distance(p1, p2)); // 5.0
In Node.js
Await initialization before calling functions:
import { initialized, Point, distance } from 'mylib';
await initialized;
const p1: Point = { x: 0, y: 0 };
const p2: Point = { x: 3, y: 4 };
console.log(distance(p1, p2)); // 5.0
In a Browser (no bundler)
Use the web entrypoint and call init():
<script type="module">
import init, { distance } from './pkg/web.js';
await init();
const p1 = { x: 0, y: 0 };
const p2 = { x: 3, y: 4 };
console.log(distance(p1, p2)); // 5.0
</script>
Use in Python
Install the wheel from dist/python/wheelhouse, then import the generated package.
python -m pip install dist/python/wheelhouse/mylib-0.1.0-*.whl
import mylib
p1 = mylib.Point(0, 0)
p2 = mylib.Point(3, 4)
print(mylib.distance(p1, p2)) # 5.0
Use in C
Enable the experimental target in boltffi.toml:
[targets.c]
enabled = true
output = "dist/c"
Then build the header and libraries:
boltffi pack c --experimental --deny-skipped
Save this as main.c:
#include <stdio.h>
#include "mylib.h"
int main(void) {
MylibPoint start = {0.0, 0.0};
MylibPoint end = {3.0, 4.0};
printf("%g\n", mylib_distance(start, end));
return 0;
}
On Linux or macOS:
cc -std=c11 main.c -I dist/c/include -L dist/c/lib -lmylib \
-Wl,-rpath,"$PWD/dist/c/lib" -o example
./example
The program prints 5. See linking a C program for Windows commands and memory management for cleanup rules.
What you learned
#[data]creates records (value types)#[export]exposes functionsboltffi packhandles build, codegen, and packaging in one step
