#[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:
- Call
initialize()to initialize this library. - Catch any panics that occur.
- If a panic does occur, the function will return
LIBRARY_FUNCTION_ERROR.
- If a panic does occur, the function will return
§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 type | Wolfram library function parameter type |
|---|---|
bool | "Boolean" |
mint | Integer |
mreal | Real |
mcomplex | Complex |
String | String |
CString | String |
&NumericArray | a. LibraryExpressionEnum[NumericArray] b. {LibraryExpressionEnum[NumericArray], "Constant"}1 |
NumericArray | a. {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 type | Wolfram library function return type |
|---|---|
() | "Void" |
bool | "Boolean" |
mint | Integer |
mreal | Real |
i8, i16, i32 | Integer |
u8, u16, u32 | Integer |
f32 | Real |
mcomplex | Complex |
String | String |
NumericArray | LibraryExpressionEnum[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.
- If a panic does occur, it will be returned as a
§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).
| 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|> *)