Skip to main content

export

Attribute Macro export 

Source
#[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).

AttributeWire formatCargo feature (on wolfram-export)
#[export]native MArgument ABInative (in the default feature set)
#[export(margs)]raw MArgument ABInative
#[export(wstp)]WSTP LinkObjectwstp
#[export(wxf)]typed WXF ByteArraywxf
# 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

Same C ABI as plain #[export], but the function receives the raw &[MArgument] arguments and MArgument return slot directly, with no FromArg/IntoArg marshaling performed for you. Use this when you need full manual control over argument/return conversion.

Since the parameter/return types aren’t visible to the macro from the function signature alone, declare them by hand with args = (..)/ ret = .. — each fragment is spliced verbatim into a wolfram_expr::expr! call, so wolfram-expr must be a direct dependency of your crate to use them:

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; }
}

This makes raw_add show up in cargo wl build’s generated Functions.wl with a real, correct LibraryFunctionLoad call — same as if you’d written LibraryFunctionLoad["...", "raw_add", {Real, Real}, Real] by hand. Omitting args/ret still compiles, but defaults the generated entry’s type spec to the same fixed LinkObject/LinkObject placeholder #[export(wstp)] uses — which a raw MArgument function does not actually accept, so calling it would misbehave — and 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 plumbing.

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|> *)