#[export]Expand description
Export a function as a Wolfram LibraryLink function. See
export for the four wire-format modes (native, margs,
wstp, wxf) and the Cargo features each one needs.
Export a function as a Wolfram LibraryLink function, in one of four wire
formats picked by a keyword argument. All four need
LibraryFunctionLoad on the Wolfram side and none of them need the
automate-function-loading-boilerplate feature to work — that feature
only affects whether cargo wl build can discover the load call for you
(#[export(margs)] needs an args =/ret = annotation for that
discovered call to be correct — see below).
| Attribute | Wire format | Cargo feature (on wolfram-export) |
|---|---|---|
#[export] | native MArgument ABI | native (in the default feature set) |
#[export(margs)] | raw MArgument ABI | native |
#[export(wstp)] | WSTP LinkObject | wstp |
#[export(wxf)] | typed WXF ByteArray | wxf |
# Cargo.toml
wolfram-export = { version = "0.6", features = ["wstp", "wxf"] } # native is on by default§#[export] — native mode (default)
Parameters and the return type must implement FromArg/IntoArg:
bool, i64, f64, String, NumericArray, and references thereof.
This is the fastest mode — arguments cross the LibraryLink ABI with no
intermediate encoding.
use wolfram_export::export;
#[export]
fn add(a: i64, b: i64) -> i64 { a + b }
#[export]
fn greet(name: String) -> String { format!("Hello, {name}!") }§#[export(margs)] — raw native mode
The function works directly on LibraryLink’s C argument type: it receives
the raw &[MArgument] slice and the MArgument return slot, exactly as
the kernel passed them. MArgument is a union of typed pointers
(integer, real, tensor, sparse, numeric, utf8string, …), so
this is the escape hatch for anything plain #[export] can’t express —
types with no FromArg/IntoArg impl, like SparseArray, or full
control over how each argument is read.
Reading a union field is unsafe: nothing checks that the field you read
matches what the kernel actually sent — that’s up to the signature the
function is loaded with.
use wolfram_export::{export, sys::MArgument};
#[export(margs, args = (::Real, ::Real), ret = ::Real)]
fn raw_add(args: &[MArgument], ret: MArgument) {
unsafe { *ret.real = *args[0].real + *args[1].real; }
}The FromArg/IntoArg conversions that plain #[export] applies
automatically are still there to call yourself, so only the argument that
actually needs raw handling has to be done by hand:
use wolfram_export::{export, sys::MArgument};
use wolfram_library_link::{FromArg, IntoArg, NumericArray};
#[export(margs,
args = (::List[::LibraryDataType["NumericArray", "Real64"], "Constant"], ::Real),
ret = ::LibraryDataType["NumericArray", "Real64"]
)]
fn scale(args: &[MArgument], ret: MArgument) {
let arr = unsafe { <&NumericArray<f64>>::from_arg(&args[0]) };
let factor = unsafe { f64::from_arg(&args[1]) };
let scaled: Vec<f64> = arr.as_slice().iter().map(|v| v * factor).collect();
unsafe { NumericArray::from_slice(&scaled).into_arg(ret) };
}For a type with no FromArg/IntoArg impl at all — reading the raw
MArgument.sparse pointer and driving the MSparseArray_* C API in rtl
directly — see margs_sparse_array_merge in
wolfram-examples-internal.
The args = (..)/ret = .. annotation declares the function’s
LibraryFunctionLoad type specs — the same {Real, Real}, Real you would
write on the Wolfram side, as expr! fragments (each is spliced verbatim
into a wolfram_expr::expr! call, so wolfram-expr must be a direct
dependency of your crate). It is not required for the function to work —
you can always call LibraryFunctionLoad yourself with the right types —
but it is what lets cargo wl build put a correct load call in the
generated Functions.wl. Without it the generated entry defaults to the
same fixed LinkObject/LinkObject placeholder #[export(wstp)] uses,
which a raw MArgument function does not actually accept, and the macro
emits a compile-time warning telling you to annotate it.
§#[export(wstp)] — WSTP mode
The function receives a Vec<Expr> (all arguments as a list) and returns
an Expr, or takes a &mut Link for low-level control over the wire.
Use this mode when the function’s arguments or return value don’t fit a
fixed native/WXF shape — e.g. variadic arguments, or streaming a result
incrementally.
use wolfram_export::export;
use wolfram_expr::{expr, Expr, ExprKind};
// High-level: Vec<Expr> in, Expr out.
#[export(wstp)]
fn reverse(args: Vec<Expr>) -> Expr {
let list = args.into_iter().next().expect("expected 1 arg");
if let ExprKind::Normal(n) = list.kind() {
let head = n.head().clone();
expr!(head[..n.elements().iter().rev().cloned()])
} else {
list
}
}§#[export(wxf)] — typed WXF mode
The generated wrapper reads a WXF-encoded ByteArray MArgument,
deserializes all arguments via FromWXF, calls your function, and
serializes the return value via ToWXF back into a ByteArray. Panics
are caught and returned as structured Failure["RustPanic", …]
expressions. Use this mode for structured arguments/return values
(structs, enums, Option/Result) without hand-writing WSTP code.
use wolfram_export::export;
use wolfram_expr::{expr, Expr};
use wolfram_serialize::{ToWXF, FromWXF};
// Primitives and Vec<T> work out of the box.
#[export(wxf)]
fn scale(values: Vec<f64>, factor: f64) -> Vec<f64> {
values.into_iter().map(|v| v * factor).collect()
}
// `Expr` implements `ToWXF`/`FromWXF` directly (in `wolfram-expr`), so a
// function can take and return untyped Wolfram Language expressions —
// useful when the shape isn't known ahead of time, or a derive isn't worth
// it. Build the result with the `expr!` macro.
#[export(wxf)]
fn add_hold(e: Expr) -> Expr {
expr!(System::Hold[e])
}
// Structs need #[derive(ToWXF, FromWXF)].
#[derive(ToWXF, FromWXF)]
struct Point { x: f64, y: f64 }
#[export(wxf)]
fn midpoint(a: Point, b: Point) -> Point {
Point { x: (a.x + b.x) / 2.0, y: (a.y + b.y) / 2.0 }
}
// Option<T> and Result<T,E> are supported too.
#[export(wxf)]
fn safe_div(a: f64, b: f64) -> Option<f64> {
if b == 0.0 { None } else { Some(a / b) }
}On the Wolfram side a struct maps to an Association:
midpoint[<|"x" -> 0.0, "y" -> 0.0|>, <|"x" -> 2.0, "y" -> 4.0|>]
(* Returns <|"x" -> 1.0, "y" -> 2.0|> *)