#[export]Expand description
Exports a Rust function to OCaml.
This procedural macro handles the complexities of the OCaml Foreign Function Interface (FFI),
allowing Rust functions to be called from OCaml code. It generates the necessary
extern "C" wrapper function and manages type conversions and memory safety.
§Basic Usage
use ocaml_interop::{OCaml, OCamlRuntime, OCamlBytes, OCamlInt, ToOCaml};
#[ocaml_interop::export]
fn process_bytes(cr: &mut OCamlRuntime, data: OCaml<OCamlBytes>) -> OCaml<OCamlInt> {
let byte_slice: &[u8] = &data.to_rust::<Vec<u8>>();
let length = byte_slice.len() as i64;
length.to_ocaml(cr)
}The macro generates an extern "C" function with the same identifier as the Rust function
(e.g., process_bytes in the example above).
§Key Features
- Automatic FFI Boilerplate: Generates the
extern "C"wrapper and handles argument/return value marshalling. - Type Safety: Utilizes types like
OCaml<T>andBoxRoot<T>to provide safe abstractions over OCaml values. - Argument Handling:
- The first argument must be
&mut OCamlRuntime(or&OCamlRuntimeifnoallocis used). OCaml<'gc, T>: For OCaml values passed as arguments. These are not automatically rooted by the macro. Their lifetime'gcis tied to the current function call’sOCamlRuntimescope. Root them explicitly (e.g., withBoxRoot<T>) if they need to persist beyond this scope or be re-passed to OCaml.BoxRoot<T>: If an argument is declared asBoxRoot<T>, the macro automatically roots the incoming OCaml value before your function body executes. This ensures the value is valid throughout the function, even across further OCaml calls.- Direct Primitive Types: Supports direct mapping for Rust primitive types like
f64,i64,i32,bool, andisizeas arguments. The OCamlexternaldeclaration must use corresponding[@@unboxed]or[@untagged]attributes.
- The first argument must be
- Return Types:
- Typically, functions return
OCaml<T>. - Direct primitive types (see above) can also be returned.
- Typically, functions return
- Panic Handling:
- By default, Rust panics are caught and raised as an OCaml exception (
RustPanic of stringif registered, otherwiseFailure). - This can be disabled with
#[ocaml_interop::export(no_panic_catch)]. Use with caution.
- By default, Rust panics are caught and raised as an OCaml exception (
- Bytecode Function Generation:
- Use
#[ocaml_interop::export(bytecode = "my_ocaml_bytecode_function_name")]to generate a wrapper for OCaml bytecode compilation. - The OCaml
externaldeclaration should then specify both the bytecode and native function names:external rust_fn : int -> int = "bytecode_stub_name" "native_c_stub_name".
- Use
noallocAttribute:#[ocaml_interop::export(noalloc)]for functions that must not trigger OCaml GC allocations.- Requires the runtime argument to be
cr: &OCamlRuntime(immutable). - Implies
no_panic_catch. Panics innoallocfunctions lead to undefined behavior. - The corresponding OCaml
externalmust be annotated with[@@noalloc]. - The user is responsible for ensuring no OCaml allocations occur in the Rust function body.
§Argument and Return Value Conventions
The macro handles the conversion between OCaml’s representation and Rust types.
§OCaml Values
OCaml<T>: Represents an OCaml value that is not yet rooted. Its lifetime is tied to the current OCaml runtime scope.BoxRoot<T>: Represents an OCaml value that has been rooted and is protected from the OCaml garbage collector. The#[ocaml_interop::export]macro can automatically root arguments if they are specified asBoxRoot<T>.
§Direct Primitive Type Mapping
For performance, certain Rust primitive types can be directly mapped to unboxed or untagged OCaml types. This avoids boxing overhead.
| Rust Type | OCaml Type | OCaml external Attribute(s) Needed |
|---|---|---|
f64 | float | [@@unboxed] (or on arg/ret type) |
i64 | int64 | [@@unboxed] (or on arg/ret type) |
i32 | int32 | [@@unboxed] (or on arg/ret type) |
bool | bool | [@untagged] (or on arg/ret type) |
isize | int | [@untagged] (or on arg/ret type) |
() | unit | (Usually implicit for return) |
Example (OCaml external for direct primitives):
external process_primitive_values :
(int [@untagged]) ->
(bool [@untagged]) ->
(float [@unboxed]) ->
(int32 [@unboxed]) =
"" "process_primitive_values"For more detailed information, refer to the user guides, particularly Exporting Rust Functions (Part 3)