Skip to main content

export

Attribute Macro export 

Source
#[export]
Expand description

Export the specified functions as native LibraryLink functions.

To be exported by this macro, the specified function(s) must implement NativeFunction.

Functions exported using this macro will automatically:

§Syntax

Export a native function.

#[export]

Export a function using the specified low-level shared library symbol name.

#[export(name = "WL_square")]

§Advanced

Don’t include an exported function in the automatic generate_loader! output.

#[export(hidden)]

§Examples

§Primitive data types

Export a native function with a single integer argument:

#[export]
fn square(x: i64) -> i64 {
    x * x
}
LibraryFunctionLoad["...", "square", {Integer}, Integer]

Export a native function with a single string argument:

#[export]
fn reverse_string(string: String) -> String {
    string.chars().rev().collect()
}
LibraryFunctionLoad["...", "reverse_string", {String}, String]

Export a native function with multiple arguments:

#[export]
fn times(a: f64, b: f64) -> f64 {
    a * b
}
LibraryFunctionLoad["...", "times", {Real, Real}, Real]

§Numeric arrays

Export a native function with a NumericArray argument:

#[export]
fn total_i64(list: &NumericArray<i64>) -> i64 {
    list.as_slice().into_iter().sum()
}
LibraryFunctionLoad[
    "...", "total_i64",
    {LibraryExpressionEnum[NumericArray, "Integer64"]}
    Integer
]

§Customize exported function name

By default, the exported name of a function exported to the Wolfram Langauge using #[export] will be the same as the Rust function name. The exported name can be customed by specifying the name = "..." argument to the #[export] macro:

use wolfram_library_link::export;

#[export(name = "native_square")]
fn square(x: i64) -> i64 {
    x * x
}
LibraryFunctionLoad["<library name>", "native_square", {Integer}, Integer]

§Parameter types

When manually writing the Wolfram LibraryFunctionLoadWL call necessary to load a Rust LibraryLink function, you must declare the type signature of the function using the appropriate types.

The following table describes the relationship between Rust types that can be used as parameter types in a native LibraryLink function (namely: those that implement FromArg) and the compatible Wolfram LibraryLink function parameter type(s).

FromArg::parameter_type() can be used to determine the Wolfram library function parameter type programatically.

If you would prefer to have the Wolfram Language code for loading your library be generated automatically, use the generate_loader! macro.

⚠️ Warning! ⚠️

Calling a LibraryLink function from the Wolfram Language that was loaded using the wrong parameter type may lead to undefined behavior! Ensure that the function parameter type declared in your Wolfram Language code matches the Rust function parameter type.

Rust parameter typeWolfram library function parameter type
bool"Boolean"
mintInteger
mrealReal
mcomplexComplex
StringString
CStringString
&NumericArraya. LibraryExpressionEnum[NumericArray]
b. {LibraryExpressionEnum[NumericArray], "Constant"}1
NumericArraya. {LibraryExpressionEnum[NumericArray], "Manual"}1
b. {LibraryExpressionEnum[NumericArray], "Shared"}1
&NumericArray<T>a. LibraryExpressionEnum[NumericArray, "..."]1
b. {LibraryExpressionEnum[NumericArray, "..."], "Constant"}1
NumericArray<T>a. {LibraryExpressionEnum[NumericArray, "..."], "Manual"}1
b. {LibraryExpressionEnum[NumericArray, "..."], "Shared"}1
DataStore"DataStore"

§Return types

The following table describes the relationship between Rust types that implement IntoArg and the compatible Wolfram LibraryLink function return type.

IntoArg::return_type() can be used to determine the Wolfram library function parameter type programatically.

Rust return typeWolfram library function return type
()"Void"
bool"Boolean"
mintInteger
mrealReal
i8, i16, i32Integer
u8, u16, u32Integer
f32Real
mcomplexComplex
StringString
NumericArrayLibraryExpressionEnum[NumericArray]
NumericArray<T>LibraryExpressionEnum[NumericArray, "..."1]
DataStore"DataStore"

Some LibraryLink types — notably SparseArray — have no FromArg/IntoArg impl and so can’t be used here. Use #[export(margs)] instead and read/write the raw MArgument.sparse (MSparseArray) pointer by hand; see the “Raw native” section above.




§#[export(wstp)]

Export the specified functions as native LibraryLink WSTP functions.

To be exported by this macro, the specified function(s) must implement WstpFunction.

Functions exported using this macro will automatically:

  • Call initialize() to initialize this library.
  • Catch any panics that occur.
    • If a panic does occur, it will be returned as a Failure[...] expression.

§Syntax

Export a LibraryLink WSTP function.

#[export(wstp)]

Export a LibraryLink WSTP function using the specified low-level shared library symbol name.

#[export(wstp, name = "WL_square")]

§Examples

§WSTP function that squares a single integer argument:
use wolfram_library_link::{export, wstp::Link};

#[export(wstp)]
fn square_wstp(link: &mut Link) {
    // Get the number of elements in the arguments list.
    let arg_count = link.test_head("List").unwrap();

    if arg_count != 1 {
        panic!("square_wstp: expected to get a single argument");
    }

    // Get the argument value.
    let x = link.get_i64().expect("expected Integer argument");

    // Write the return value.
    link.put_i64(x * x).unwrap();
}
LibraryFunctionLoad["...", "square_wstp", LinkObject, LinkObject]
§WSTP function that computes the sum of a variable number of arguments:
use wolfram_library_link::{export, wstp::Link};

#[export(wstp)]
fn total_args_i64(link: &mut Link) {
    // Check that we recieved a functions arguments list, and get the number of arguments.
    let arg_count: usize = link.test_head("List").unwrap();

    let mut total: i64 = 0;

    // Get each argument, assuming that they are all integers, and add it to the total.
    for _ in 0..arg_count {
        let term = link.get_i64().expect("expected Integer argument");
        total += term;
    }

    // Write the return value to the link.
    link.put_i64(total).unwrap();
}
LibraryFunctionLoad["...", "total_args_i64", LinkObject, LinkObject]

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

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

  1. The Details and Options section of the Wolfram Language NumericArray reference page lists the available element types. ↩ 1 2 3 4 5 6 7 8