Expression Evaluation Helpers
Safe wrappers for building and evaluating R function calls from Rust.
Safe wrappers for building and evaluating R function calls from Rust.
πTypes
| Type | Purpose |
|---|---|
RSymbol | Interned R symbol (SYMSXP) β never GCβd |
RCall | Builder for R function calls (LANGSXP) |
REnv | Well-known R environments (Global, Base, Empty) |
πQuick Example
use miniextendr_api::expression::{RCall, REnv};
use miniextendr_api::ffi::Rf_mkString;
unsafe {
// Call paste0("hello", " world") in base
let result = RCall::new("paste0")
.arg(Rf_mkString(c"hello".as_ptr()))
.arg(Rf_mkString(c" world".as_ptr()))
.eval(REnv::base().as_sexp())?;
}πRSymbol
Wraps Rβs Rf_install() for interned symbols. Symbols are never garbage collected, so RSymbol needs no GC protection.
use miniextendr_api::expression::RSymbol;
// From a Rust string (allocates a CString)
let sym = unsafe { RSymbol::new("my_var") };
// From a C string literal (zero allocation)
let sym = unsafe { RSymbol::from_cstr(c"my_var") };
// Use as SEXP
let sexp = sym.as_sexp();πRCall
Builds R function calls with positional and named arguments.
use miniextendr_api::expression::RCall;
unsafe {
// Positional arguments
let result = RCall::new("sum")
.arg(my_vector_sexp)
.eval(env)?;
// Named arguments
let result = RCall::new("paste")
.arg(x_sexp)
.arg(y_sexp)
.named_arg("sep", sep_sexp)
.eval(env)?;
}πError Handling
eval() uses R_tryEvalSilent and returns Result<SEXP, String>. On failure, the error message comes from Rβs geterrmessage().
match RCall::new("stop").arg(msg_sexp).eval(env) {
Ok(result) => { /* success */ },
Err(msg) => { /* msg contains R's error message */ },
}πGC Protection
RCall protects all intermediate SEXPs (the call object and argument list) during construction. The returned SEXP is unprotected β caller must protect it if it will survive across R API calls.
πREnv
Provides handles to Rβs well-known environments:
use miniextendr_api::expression::REnv;
let global = REnv::global(); // R_GlobalEnv
let base = REnv::base(); // R_BaseEnv
let empty = REnv::empty(); // R_EmptyEnv
let base_ns = REnv::base_namespace(); // R_BaseNamespace (for .Internal etc.)
let methods = REnv::package_namespace("methods"); // getNamespace("methods")
let caller = REnv::caller(); // calling environment (R_GetCurrentEnv)
// Use as SEXP
let sexp = base.as_sexp();
Prefer package_namespace(pkg) over chasing symbols through
R_GlobalEnv. The former mirrors getNamespace(pkg) and resolves
against the packageβs own namespace regardless of what the user has
attached on the search path. eval_global() has been removed; evaluate
in base(), base_namespace(), or the callerβs env instead.
πSafety Requirements
All functions in this module require:
- Being called from the R main thread (they use R API calls)
unsafeblocks (they call into C)
In #[miniextendr] functions, use these inside unsafe(main_thread) blocks or within ALTREP callbacks (which already run on the main thread).
πUse Cases
- S4 slot access: The
s4_helpersmodule usesRCallinternally - Calling R functions from ALTREP callbacks: When
elt()needs to call R - Dynamic dispatch: Building R function calls based on runtime data
- Package interop: Calling functions from other R packages
πSee Also
- CLASS_SYSTEMS.md β S4 helpers built on RCall
- THREADS.md β Main thread requirements
- GC_PROTECT.md β Protecting returned SEXPs