Expand description
Condition macros and signal enum for the Rust→R condition pipeline.
This module provides two things:
-
RConditionenum — the internal panic payload used byerror!(),warning!(),message!(), andcondition!()macros. Caught bycrate::unwind_protect::with_r_unwind_protectbefore the generic panic→error path, then forwarded to R as a structured condition withrust_*class layering viacrate::error_value::make_rust_condition_value. -
AsRErrorstruct — wraps anyE: std::error::Errorand preserves the full error chain (cause/source) when converting to an R error message. Use as theErrtype inResultreturns.
§When to reach for what
There are three Rust→R error-emission paths and they are not
interchangeable. The crate-level rationale (why tagged-SEXP at all, what
error_in_r defaults imply, and the with_r_unwind_protect leak) lives
on crate::error_value; the practical picking-one summary:
panic!— escape hatch. Becomes classc("rust_error", "simpleError", "error", "condition")withkind = "panic". Use for genuine bugs or impossible states. Cheapest in source; coarsest in R (callers can only matchrust_error/error, not a specific class).error!/warning!/message!/condition!(this module) — typed conditions. Same transport, but allow an optionalclass = "name"so R-sidetryCatchcan route by class.warning!/message!/condition!are the only way to emit non-error conditions;panic!is always an error.Result<T, E>withAsRError<E>— value-style propagation through Rust code. Converts at the boundary;kind = "result_err". Best when the failure path is real-and-recoverable in Rust and the error chain (std::error::Error::source) is worth preserving.
Rf_error is not on this list. Direct Rf_error skips Rust
destructors unconditionally and is forbidden by lint MXL300; see
crate::error_value for the full reasoning.
§Macro-vs-module name collision
#[macro_export] puts each macro at the crate root, where error! and
condition! collide with the same-named modules pub mod error and pub mod condition. The practical implication: use miniextendr_api::{error, condition} imports the modules, not the macros, and a subsequent
error!(...) call fails to resolve.
Workarounds, in rough order of ergonomics:
- Invoke via fully-qualified path:
miniextendr_api::error!("..."). use miniextendr_api as mx;thenmx::error!("...").warning!andmessage!have no module conflict —use miniextendr_api::{warning, message};works directly.
See the individual macro docs for the per-macro reminder.
§Condition macros
The four macros are the user-facing API for raising non-panic conditions from
Rust. They ride the tagged-condition transport that every #[miniextendr]
function uses:
use miniextendr_api::{error, warning, message, condition};
#[miniextendr]
fn demo_error() {
error!("something went wrong: {}", 42);
}
#[miniextendr]
fn demo_warning() {
warning!("something looks suspicious");
}
#[miniextendr]
fn demo_message() {
message!("progress: {} of {}", 1, 10);
}
#[miniextendr]
fn demo_condition() {
condition!("a signallable condition");
}Optional class = extension for programmatic catching:
#[miniextendr]
fn typed_error(name: &str) {
error!(class = "my_error", "missing field: {name}");
}tryCatch(typed_error("x"), my_error = function(e) "caught!")
# [1] "caught!"Optional data = extension attaches structured fields readable as
e$<name> in handlers (rlang-abort()-style):
#[miniextendr]
fn validate(value: i32) {
if !(0..=100).contains(&value) {
miniextendr_api::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 100Supported data value types (anything with RValue: From<_>): scalars and
Vecs of i32, f64, bool, String / &str; their NA-aware Option /
Vec<Option<_>> forms (None → R NA); the wide-integer ladder (i64 /
u32, narrowed to integer(1) when it fits, double(1) otherwise); and the
RValue::debug escape hatch, which stringifies any
T: Debug. For nested lists or complex/raw/NA-bearing values build an
RValue directly. The payload is built as a Send-safe owned
value at the call site and materialised as R objects on the main thread — so
data = works from worker-thread code too.
Three data = grammars are accepted (see crate::error!):
- single pair:
data = ("name", value) - bracketed list:
data = [("a", v1), ("b", v2)] - keyed builder sugar:
data = { value = 42, code = 7 }(bare-ident keys)
§AsRError
use miniextendr_api::condition::AsRError;
#[miniextendr]
fn parse_config(path: &str) -> Result<i32, AsRError<std::io::Error>> {
let content = std::fs::read_to_string(path).map_err(AsRError)?;
Ok(content.len() as i32)
}Structs§
- AsRError
- Structured error wrapper that preserves the
std::error::Errorcause chain.
Enums§
- RCondition 👻
- Internal panic payload for structured R conditions.
Functions§
- repanic_
if_ ⚠rust_ error - Inspect a SEXP returned by a trait-ABI vtable shim and, if it is a tagged
error value, re-panic with the reconstructed
RCondition.
Type Aliases§
- Condition
Data - Named condition-data payload: an ordered list of
(name, value)pairs.