Records
Records are value types: structs and enums marked with #[data]. When a record crosses the boundary, it gets copied. The target language receives its own copy of the data.
Classes are different. A class lives in Rust, and the target language holds a reference to it. See Classes for when to use one over the other.
Structs
Mark a struct with #[data] and it becomes a value type that can cross the FFI boundary. The caller creates an instance, passes it in, and BoltFFI handles the transfer.
The cost of passing a struct depends entirely on its contents. A struct with only primitive fields is nearly as fast as passing primitives directly. The bytes pack together and move across the boundary with no per-field overhead. This is the zero-copy path that makes BoltFFI fast.
A struct containing strings or collections takes longer to move across the boundary. Each string means an allocation on the receiving side. Each collection means transferring its length and all elements. Still fast in absolute terms, but noticeably slower than primitive-only structs in tight loops.
Primitive-only structs
The bytes pack together and move across the boundary with no per-field overhead. This is the fast path.
Structs with strings or collections
These take longer to cross the boundary. A struct with two strings means two allocations on the receiving side. Still fast for normal use, but measure if you’re calling thousands of times per second.
Nested structs
A struct can contain other structs, and BoltFFI handles them recursively. There’s no depth limit imposed by BoltFFI, though deeply nested structures take more time to move across the boundary.
Optional fields
Option<T> in Rust becomes a nullable type in the target language. None becomes null or nil on the other side.
Default values
Use #[boltffi::default(...)] on a field to give it a default value. The generated constructor will have that parameter as optional.
Supported default values
| Value | Example |
|---|---|
| Booleans | #[boltffi::default(true)] |
| Integers | #[boltffi::default(42)] |
| Floats | #[boltffi::default(2.5)] |
| Strings | #[boltffi::default("hello")] |
| Enum variants | #[boltffi::default(Status::Pending)] |
| None | #[boltffi::default(None)] |
Option fields automatically default to nil/null if no explicit default is provided.
Enums
Enums in Rust are more powerful than enums in most languages. A Rust enum can have variants with no data, variants with different data types, or any combination. BoltFFI handles all of these cases, generating the appropriate type in each target language.
Simple enums
Enums with no associated data become native enum types that you can switch on, compare, or pass around.
Enums with associated data
Each variant can carry different data. A loading state might carry progress; an error state might carry a message and code.