Skip to main content

Failure

Derive Macro Failure 

Source
#[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 variantWL expression
UnitFailure["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 },
}