#[derive(Failure)]
{
// Attributes available to this derive:
#[wolfram]
}
Expand description
Derive From<YourEnum> for Expr, mapping each variant to a Wolfram
Failure
expression.
§Wire format
Each enum variant becomes:
| Rust variant | WL expression |
|---|---|
Unit | Failure["Unit", <||>] |
WithMessage(String) | Failure["WithMessage", <|"0" -> "…"|>] |
Named { code: i64 } | Failure["Named", <|"code" -> …|>] |
§Idiomatic pattern: one error enum per exported library
Define a single error enum for your library, derive Failure on it, and
return Result<T, YourError> from every #[export(wxf)] function. The
macro wires the Err path automatically — Rust’s ? operator propagates
errors from helpers, and the kernel always gets a properly structured
Failure[…] it can pattern-match on.
ⓘ
use wolfram_export::export;
use wolfram_serialize::{Failure, ToWXF, FromWXF};
/// All errors this library can return to Wolfram.
#[derive(Failure, Debug)]
enum LibError {
/// Failure["KeyNotFound", <|"key" -> "…"|>]
KeyNotFound { key: String },
/// Failure["ParseError", <|"input" -> "…", "reason" -> "…"|>]
ParseError { input: String, reason: String },
/// Failure["OutOfRange", <|"value" -> …, "min" -> …, "max" -> …|>]
OutOfRange { value: f64, min: f64, max: f64 },
/// Failure["Unsupported", <||>]
Unsupported,
}
// Helper that returns a domain error — ? propagates it automatically.
fn lookup(map: &std::collections::HashMap<String, f64>, key: &str)
-> Result<f64, LibError>
{
map.get(key)
.copied()
.ok_or_else(|| LibError::KeyNotFound { key: key.into() })
}
#[derive(ToWXF, FromWXF)]
struct Stats { mean: f64, count: i64 }
// Wolfram calls: computeStats[<|"a" -> 1.0, "b" -> 2.0|>, "a"]
// On success returns Stats as an Association.
// On failure returns Failure["KeyNotFound", <|"key" -> "a"|>] etc.
#[export(wxf)]
fn compute_stats(
data: std::collections::HashMap<String, f64>,
key: String,
) -> Result<Stats, LibError> {
let value = lookup(&data, &key)?; // propagates KeyNotFound
if !(0.0..=1e9).contains(&value) {
return Err(LibError::OutOfRange { value, min: 0.0, max: 1e9 });
}
Ok(Stats { mean: value, count: data.len() as i64 })
}§Handling errors on the Wolfram side
The kernel receives the Failure object. Standard WL idioms work directly:
result = computeStats[data, "missingKey"];
(* Check for failure *)
FailureQ[result] (* True *)
(* Pattern match on the specific error type *)
Switch[result,
Failure["KeyNotFound", assoc_],
Print["Key not found: ", assoc["key"]],
Failure["OutOfRange", assoc_],
Print["Value ", assoc["value"], " outside [", assoc["min"], ", ", assoc["max"], "]"],
_,
Print["Unexpected error: ", result]
]
(* Or use Quiet + Check for simple fallback logic *)
value = Check[computeStats[data, key], $Failed];§Combining with std::error::Error
Derive both Failure and thiserror::Error to get a type that works as a
proper Rust error (for ? chains, logging, tests) and converts cleanly
to WL when returned across the FFI boundary:
use wolfram_serialize::Failure;
#[derive(Failure, Debug, Clone, thiserror::Error)]
enum DbError {
#[error("connection refused: {addr}")]
ConnectionRefused { addr: String },
#[error("query timeout after {ms}ms")]
Timeout { ms: i64 },
}