pub unsafe fn eval_with_handlers(expr: SEXP, env: SEXP) -> SEXPExpand description
Evaluate expr in env in the caller’s R context, the way R code calling
eval(expr, env) does.
RCall::eval, r_eval_str | eval_with_handlers | try_eval_with_handlers | |
|---|---|---|---|
| R entry point | R_tryCatchError inside R_ToplevelExec | Rf_eval in its own R_UnwindProtect | R_tryCatchError in its own R_UnwindProtect |
caller’s withCallingHandlers(), suppressWarnings() | not seen (R_ToplevelExec empties the handler and restart stacks) | see every warning, message and condition | see every warning, message and condition except an error |
| R error | Err(REvalError): R’s condition, with its message (no Error in prefix, no Calls: line), call and classes; the Rust code decides what to raise | unwinds as the R condition, class and call kept | Err(REvalError), as from RCall::eval |
caller’s tryCatch(warning = ), restarts, interrupts | not seen (an interrupt ends the evaluation with an Err) | exit through the Rust frames | exit through the Rust frames |
An R error, or any other exit from the evaluation (an exiting handler of
the caller’s tryCatch(), invokeRestart(), an interrupt), stops at this
function’s own R_UnwindProtect frame, so it never jumps over a Rust
frame. It then leaves as a Rust unwind: the frames between here and the
#[miniextendr] boundary drop their values (a guard’s Drop runs), and
the boundary hands the exit back to R, which carries on to the handler or
restart it was going to, with the original condition object. A warning or
message the caller muffles returns here and the evaluation continues.
So this is the evaluator for code a user wrote: an argument passed
unevaluated (crate::Quoted, crate::Quosure), a callback, a call into
a package whose conditions are part of its interface. Keep RCall::eval
for internal calls whose failure the Rust code handles itself, for
instance by raising its own condition with the caught message
(REvalError::reraise_class). try_eval_with_handlers combines the
two: the caller’s handlers see warnings and messages, and an R error comes
back as Err for the Rust code to raise.
The unwind must reach a miniextendr boundary. Don’t catch it with
std::panic::catch_unwind in between, or the R exit is abandoned (R
carries on as after R_tryEval, without running the caller’s handler);
resume a caught payload with std::panic::resume_unwind on the same
thread before the call returns. An exit resumed in a later call has lost
its target: the boundary raises an error instead. Don’t call this from a
Drop implementation: an exit there would start an unwind during an
unwind, which aborts.
§Safety
- R’s main thread, inside a miniextendr boundary: a
#[miniextendr]function body, awith_r_unwind_protectclosure, an ALTREP callback with ther_unwindorrust_unwindguard (notunsafe, which catches nothing), a connection callback, awith_r_threadclosure. exprandenvstay rooted for the call;envis an environment.
§Returns
The value, unprotected: protect it before the next allocation.