macro_rules! error {
($($t:tt)*) => { ... };
}Expand description
Raise an R error from Rust with rust_error class layering.
Rides the tagged-condition transport that every #[miniextendr] function uses.
The raised condition has class c("rust_error", "simpleError", "error", "condition").
An optional class = "name" form prepends a custom class for programmatic catching:
c("name", "rust_error", "simpleError", "error", "condition"). class also
takes a vector (class = ["pkg_error_missing_field", "pkg_error"], or any
ConditionClass value) so handlers can catch the family or the member.
The framework layers (rust_error, simpleError, error, condition)
always follow the user classes; no option leaves them out. Handlers and
inherits() see the user classes as on an R errorCondition(); a test
that compares the whole class vector with an R implementation’s compares
the leading classes instead.
§Structured data = ... payloads
An optional data = ... form (after class, before the message) attaches
named fields to the condition object, rlang-abort()-style. Handlers read
them as e$<name> instead of parsing the message string:
// Single field:
mx::error!(class = "range_error", data = ("value", value), "value {value} out of range");
// Multiple fields (bracketed list of pairs):
mx::error!(
class = "validation_error",
data = [("value", value), ("min", 0), ("max", 100)],
"value {value} out of range"
);
// Keyed builder sugar (bare-ident keys, stringified by the macro):
mx::error!(
class = "validation_error",
data = { value = value, min = 0, max = 100 },
"value {value} out of range"
);tryCatch(validate(150L), validation_error = function(e) c(e$value, e$min, e$max))
# [1] 150 0 100Argument order is fixed: call = none (optional), then class = ...
(optional), then data = ... (optional), then the format message.
Field names message, call and kind are the condition’s own slots and
are rejected (at compile time for literal / bare-identifier names, at
runtime otherwise); give the field another name (rule instead of kind,
say). A downstream error type with such a field renames it where the
payload is built (#[serde(rename)], a different key).
Supported value types: scalars and Vecs of i32, f64, bool, and
String (plus &str / Vec<&str>, converted to owned); their NA-aware
Option / Vec<Option<_>> forms (→ R NA); the wide-integer ladder (i64
/ u32); and the RValue::debug escape hatch for
any T: Debug. The payload must be Send — it travels through panic_any
and may cross the worker→main thread boundary, so live SEXPs cannot ride
along; the R objects are materialised on the main thread at the unwind
boundary. For nested lists or complex/raw values build an
RValue directly.
§Conditions without a call
A leading call = none raises the condition with no call, like R’s
stop(..., call. = FALSE): conditionCall(e) is NULL and the error
prints as Error: <message> instead of Error in f(...) : <message>. Use
it for a condition about the data rather than about the call. Classes and
data are unchanged:
mx::error!(call = none, class = "pkg_bad_row", data = { row = row }, "row {row} is malformed");The same marker exists as RError::without_call for Result errors and
as #[condition(call = none)] on a #[derive(RCondition)] type or
variant; see ConditionCall.
§See also
crate::warning!/crate::message!/crate::condition!— the non-error sibling kinds (warning continues execution; message is muffled bysuppressMessages; condition is silent without a handler).std::panic!— escape hatch with the samerust_errorclass layering but no custom-class slot. Use for true bugs / impossible states; reach forerror!when callers might want to route by class.AsRError— wrapsResult<_, E: std::error::Error>for value-style propagation through Rust code; converts at the boundary.crate::error_value— module-level rationale for the tagged-SEXP transport and theerror_in_rdefault.
Name-collision note. Because pub mod error exists at the crate root,
use miniextendr_api::error imports the module rather than this macro, and
glob imports (use miniextendr_api::*;) hit the same shadow. Prefer the
collision-free alias crate::rust_error!, which has the identical
expansion; otherwise invoke via miniextendr_api::error!(...) (fully
qualified) or mx::error!(...) after use miniextendr_api as mx;.
§Examples
use miniextendr_api as mx;
#[miniextendr]
fn fail() {
mx::error!("something went wrong: {}", 42);
}
// With a custom class for tryCatch:
#[miniextendr]
fn typed_fail(name: &str) {
mx::error!(class = "my_error", "missing field: {name}");
}tryCatch(fail(), rust_error = function(e) conditionMessage(e))
# [1] "something went wrong: 42"
tryCatch(typed_fail("x"), my_error = function(e) "caught!")
# [1] "caught!"