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§
- ArgError
- An argument error as a value: the parameter’s R name and the message.
ArgError::raiseraises it as the generated wrapper’s own argument error, throughcrate::arg_error!. - AsRError
- Structured error wrapper that preserves the
std::error::Errorcause chain. - ErrParts 👻
- The parts of a condition the generated
Errarms hand tocrate::error_value::result_err_condition_value/crate::error_value::conversion_condition_value, and thatRCondition::into_partssplits a payload into: message, user classes,datafields and theConditionCallchoice. - RError
- A ready-made classed error value for
Result<T, RError>returns. - Serde
Error Shape - The options of
#[miniextendr(serde_error(..))]as a value: declared once, shared by every function that returns one error type. - Shape
Message 🔒 - A const-evaluation panic message.
const fncan’tformat!, so the parts are copied into a fixed buffer.
Enums§
- Condition
Call - Whether a condition reports a call (
conditionCall()): the per-condition equivalent of R’swarning(..., call. = FALSE)/stop(..., call. = FALSE). - RCondition
- Owned payload for structured R conditions.
Constants§
- CONVERSION_
ERROR_ 👻CLASS_ MARKER - The class that stands for the crate’s
conversion_error_classin a tagged condition value (#1740). - CONVERSION_
PARAM_ 🔒FIELD - The name of the structured field that carries the failing parameter’s R
name on every argument-conversion condition (
e$param). - CONVERSION_
RUST_ 🔒TYPE_ FIELD - The name of the structured field that carries the Rust type an argument
failed to convert to (
e$rust_type, #1591). - PARENT_
FIELD 🔒 - The
datafield under whichRCondition::with_parentsends a cause to R; the raise helper turns it into the condition objecte$parent. - RESERVED_
CONDITION_ FIELDS - Condition-data field names that would overwrite the condition object’s own
slots when the R helper splices
datain (utils::modifyListoverlist(message, call, kind)). - SHAPE_
TYPE_ 🔒OPTIONS - The
as_*options, in the order ofSerdeErrorShape’s type lists.
Traits§
- Condition
Class - What the condition macros’
class = …(andRError::class) accept: one class or several, as&str/String/ arrays /Vec/ slices. - Conversion
ArgMessage 👻Builtin - Whole-message probe, built-in arm: a
match_argchoice error, as aMatchArgErroror at the top of aSexpError(#[derive(MatchArg)]’sTryFromSexpreturns one), words the whole argument error the way the wrapper’s R-sidematch_argcheck (.miniextendr_match_arg) does, from the sameMatchArgError::arg_error(#1767):'mode' should be one of "fast", "slow",'mode' must be of length 1,'mode' must be NULL or a character vector. So amatch_argtype converted without#[miniextendr(match_arg)]and one checked by the wrapper raise the same message. - Conversion
ArgMessage 👻Classed - Whole-message probe, classed arm: any
RConditionDataanswers with itsargument_message, so a type’s own conversion error can word the whole argument error (#1833). Same autoref level asConversionArgMessageBuiltin, likeConversionErrClassed: no built-in error type implementsRConditionData. - Conversion
ArgMessage 👻None - Whole-message probe, fallback arm: any other error leaves the message to the prefix and the error’s reason.
- Conversion
ErrBuiltin 👻 - Conversion probe, built-in arm: the framework’s own conversion errors
(
SexpErrorand theSexpTypeError/SexpClassError/SexpLengthError/SexpNaErrorit wraps, andMatchArgError), reworded for R users (#1591). - Conversion
ErrClassed 👻 - Conversion probe, preferred arm:
E: RConditionData. - Conversion
ErrDisplay 👻 - Conversion probe, fallback arm: any
E: Display. The message is the error’sDisplaytext, with no class and no data, so the condition keeps the plainrust_errorclass vector. - Conversion
Expect 👻Builtin - Expectation probe, built-in arm: an error that knows what the value should
have been. A
match_argchoice error (MatchArgError, or aSexpErrorcarrying one, as#[derive(MatchArg)]’sTryFromSexpreturns) names its choices:one of "fast", "slow"; a class error names the class (a data frame); anEitherwhose arms both refused the kind of value names both (one of "fast", "slow", or numeric). - Conversion
Expect 👻None - Expectation probe, fallback arm: any other error type says nothing about what the value should have been.
- ErrParts
Classed 👻 - Autoref-specialisation probe, first stage, preferred arm:
E: RConditionData. - ErrParts
Debug 👻 - Second stage, fallback arm: any
E: Debug(the historicalformat!("{e:?}")rendering, no class, no data). - ErrParts
Serde 👻 - Second stage, preferred arm:
E: Serialize + Displayunder theserdefeature, classed via [serde_err_parts] with the defaults (kindtag, the<crate>_errorprefix the macro passes in, no shapes).#[miniextendr(serde_error(..))]bypasses the probe with explicit options. - ErrParts
Unclassed 👻 - First stage, fallback arm: no
RConditionDataimpl. - RCondition
Data - Give a
Result<T, E>error type an R class vector and structured fields.
Functions§
- arg_
check_ 👻parts - The parts of an argument check that failed in Rust after the conversion:
#[miniextendr(no_na)]on a reading marker (AsNumeric*,AsCharacter*) whose converted value holds anNAthat R’sanyNA()did not see (the hiddenTryFromSexp::__mx_has_nasays so), or on anEither, which has no R guard, anNAthe arm taken refuses (TryFromSexp::__mx_input_has_na). - builtin_
reason_ 🔒parts - The R-facing reason for a built-in conversion error, see
ConversionErrBuiltin. - catch_
condition - Run
f, returning a condition raised inside it asErrinstead of letting it reach R; any other unwind carries on. - check_
condition_ data - Apply
check_condition_field_nameto every field of a payload. - check_
condition_ field_ name - Runtime half of the reserved-name check, for computed field names (the
macros’
exprpath andRConditionData::dataon user types). Panics whennameis reserved; the panic becomes a plainrust_errorin R, replacing the former silent overwrite. - check_
field_ 🔒list - A field list: set once, not empty, each name non-empty and listed once.
- conflict 🔒
- const_
contains 🔒 - conversion_
err_ 👻parts - Combine an argument-conversion error’s own parts with the wrapper’s parameter context.
- conversion_
prefix 👻 - The message prefix of a conversion failure whose expectation comes from
the error at run time (see
crate::__mx_conversion_expectation!):'<p>' must be <expected>('<p>' must be NULL or <expected>for anOption<_>parameter,nullable), elseinvalid '<p>' argument. - crate_
conversion_ 🔒error_ class - The package crate’s
conversion_error_class, from the entryminiextendr_init!registers inMX_CONVERSION_ERROR_CLASS(each package links its own copy of this crate, so it is that package’s), or on wasm32 from thewasm_registry.rssnapshot (#1768). Empty when the crate sets none. - fail 🔒
- Panic with
message.#[track_caller]here and on every caller puts the compile error at the user’sconst, not in this crate. - is_
no_ 🔒call_ marker truefor the tagged value’s “no call” marker in thecallslot: a length-1 logicalFALSE(ConditionCall::None). A call object orNULLis not the marker.- is_
reserved_ condition_ field trueforRESERVED_CONDITION_FIELDS.constso the macros can reject a literal field name at compile time:- 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. - resolve_
conversion_ 🔒error_ class_ marker classwithCONVERSION_ERROR_CLASS_MARKERreplaced bycrate_classes, in place, as the generated.miniextendr_raise_conditionreplaces it in R. A raising guard has no generated R to do it, so it calls this withcrate_conversion_error_class.- scalar_
option 🔒 tag/prefix: set once, never empty.- set_
twice 🔒 - str_eq 🔒
a == b, usable inconst fn.
Type Aliases§
- Condition
Data - Named condition-data payload: an ordered list of
(name, value)pairs.
Derive Macros§
- RCondition
#[derive(RCondition)]:RConditionDatagenerated from a type’s shape (#[condition(class = "…")],#[condition(message = "…")], therename/skip/debugfield options) andFrom<T>forRConditionwith the declared kind (#[condition(kind = …)], defaulterror). Derive an R condition type for a struct or enum:RConditionData(the R class vector, the message and thedatafields handlers read ase$<name>) andFrom<Self>forminiextendr_api::RCondition.