Skip to main content

RCall

Struct RCall 

Source
pub struct RCall {
    fun: SEXP,
    args: Vec<(Option<CString>, SEXP)>,
    roots: ProtectPool,
}
Expand description

Builder for constructing and evaluating R function calls.

RCall constructs a LANGSXP (R language object) from a function name or SEXP and a sequence of arguments (optionally named). It handles GC protection during construction and evaluation.

§Example

ⓘ
use miniextendr_api::expression::RCall;
use miniextendr_api::sys;

unsafe {
    // seq_len(10)
    let result = RCall::new("seq_len")
        .arg(SEXP::scalar_integer(10))
        .eval_base()?;

    // paste(x, collapse = ", ")
    let result = RCall::new("paste")
        .arg(some_sexp)
        .named_arg("collapse", sys::Rf_mkString(c", ".as_ptr()))
        .eval_base()?;
}

Fields§

§fun: SEXP

Function symbol or SEXP.

§args: Vec<(Option<CString>, SEXP)>

Arguments as (optional_name, value) pairs.

§roots: ProtectPool

Roots the callable and every argument for the builder’s lifetime, except symbols, which need no root (needs_root).

Besides keeping inline allocations alive, ProtectPool makes RCall !Send + !Sync: construction, mutation, and drop stay on R’s thread.

Implementations§

Source§

impl RCall

Source

unsafe fn from_callable(fun: SEXP) -> Self

Initialize an empty builder calling fun, rooted unless it is a symbol.

fun may be a freshly allocated, unprotected closure. The temporary stack root keeps it alive while the pool allocates its backing VECSXP.

Source

pub unsafe fn new(fun_name: &str) -> Self

Start building a call to a named R function.

The function is looked up via Rf_install, which returns an interned symbol.

§Safety

Must be called from the R main thread.

§Panics

Panics if fun_name contains a null byte.

Source

pub unsafe fn from_cstr(fun_name: &CStr) -> Self

Start building a call to a function given as a C string literal.

More efficient than new when a &CStr is available.

§Safety

Must be called from the R main thread.

Source

pub unsafe fn from_sexp(fun: SEXP) -> Self

Start building a call with a function SEXP (closure, builtin, etc.).

§Safety

fun must be a valid SEXP representing a callable R object.

Source

fn root(&mut self, value: SEXP)

Root an argument for the builder’s lifetime, unless it is a symbol.

value may be fresh: the pool keeps it reachable while it grows.

Source

pub fn arg(self, value: SEXP) -> Self

Add a positional argument.

Source

pub fn named_arg(self, name: &str, value: SEXP) -> Self

Add a named argument.

§Panics

Panics if name contains a null byte.

Source

pub fn arg_value(self, value: impl IntoR) -> Self

Convert a Rust value and add it as a positional argument.

The converted value is rooted before anything else allocates, so a value made only for this call needs no root of its own. Write RCall::new("qt").arg_value(p).arg_value(df), not p.into_sexp() and df.into_sexp() first: the second conversion, and new, allocate while the first value is not rooted.

§Panics

Panics if the conversion fails, as IntoR::into_sexp does.

Source

pub fn named_value(self, name: &str, value: impl IntoR) -> Self

Convert a Rust value and add it as a named argument. See arg_value.

§Panics

Panics if name contains a null byte, or if the conversion fails.

Source

pub fn dots(self) -> Self

Add R’s ... at this position: the dots of the frame the call is evaluated in, each passed on as the promise R matched, unevaluated.

Only LazyDots::forward / try_forward evaluate a call in a frame that has dots (the generated wrapper’s), and they add ... last themselves, so this is for putting it elsewhere: RCall::new("f").dots().named_value("na.rm", true) is f(..., na.rm = TRUE). Evaluated in an environment without dots, R raises “‘…’ used in an incorrect context”.

Source

pub(crate) fn has_dots(&self) -> bool

Whether the call passes ... (dots).

Source

pub fn quoted_arg(self, value: SEXP) -> Self

Add a positional argument passed as is: quote(<value>).

The call evaluates its arguments, so a symbol, a call or a quosure added with arg is evaluated before the function sees it. Use this for a language object the function must receive as a value: a call for an error_call / call argument, an expression for eval(). Any other value passes through quote() unchanged.

Source

pub fn named_quoted_arg(self, name: &str, value: SEXP) -> Self

Add a named argument passed as is: name = quote(<value>). See quoted_arg.

§Panics

Panics if name contains a null byte.

Source

pub unsafe fn build(&self) -> SEXP

Build the LANGSXP without evaluating it.

The returned SEXP is unprotected. The caller must protect it if further allocations will occur before use.

§Safety

Must be called from the R main thread. The builder keeps the callable and every argument rooted until it is dropped.

Source

pub unsafe fn eval(&self, env: SEXP) -> Result<SEXP, REvalError>

Evaluate the call in the given environment, catching an R error.

The call runs in a new top-level context (R_ToplevelExec) under an exiting handler for error conditions (R_tryCatchError). So the caller’s condition handlers and restarts don’t see it, and an R error comes back as Err holding R’s condition (its message, call, classes and the object itself) instead of jumping through the Rust frames. Use eval_with_handlers for a call whose conditions the user should see as raised, and try_eval_with_handlers for one whose warnings and messages the user should see but whose error the Rust code handles.

ⓘ
match RCall::new("print").arg(df).named_arg("na.print", one).eval_base() {
    Ok(_) => {}
    // `e` is R's condition: "invalid 'na.print' specification",
    // without the "Error in print.default(...) :" prefix.
    Err(e) => rust_error!(class = e.reraise_class("pkg_print_error"), "{e}"),
}
§Safety
  • Must be called from the R main thread.
  • env must be a valid ENVSXP.
  • The builder must not outlive the active R session.
§Returns
  • Ok(SEXP) with the result (unprotected — caller should protect if needed)
  • Err(REvalError) with R’s error condition on failure
Source

pub unsafe fn eval_base(&self) -> Result<SEXP, REvalError>

Evaluate in R_BaseEnv.

§Safety

Same as eval.

Source

pub unsafe fn eval_as<T>(&self, env: SEXP) -> Result<T, REvalError>
where T: TryFromSexp, T::Error: Display,

Evaluate the call in env, as eval does, and read the result as T.

The result stays rooted while it is converted, so the caller never holds it unrooted:

ⓘ
let q: Vec<Option<f64>> = RCall::new("qt")
    .arg_value(p)
    .arg_value(df)
    .eval_as(stats.as_sexp())?;

A result that T can’t read is an Err too: a simpleError whose message is the conversion’s, with no call. A T that only views the value (SEXP, List, DataFrame) comes back unrooted, as from eval; read into an owned type (Vec<T>, String, a struct) or protect it.

§Safety

As eval.

Source

pub unsafe fn eval_base_as<T>(&self) -> Result<T, REvalError>
where T: TryFromSexp, T::Error: Display,

eval_as in R_BaseEnv.

§Safety

As eval.

Source

pub unsafe fn eval_with_handlers(&self, env: SEXP) -> SEXP

Evaluate the call in env in the caller’s R context, keeping R’s condition handlers: see eval_with_handlers.

Use it to call R code that signals conditions the user should see, with their classes, through their own withCallingHandlers() / tryCatch(). A tidyselect selection, say:

ⓘ
// `cols` is a `Quosure` parameter, `data` a data frame.
let selected = unsafe {
    RCall::namespaced("tidyselect", "eval_select")?
        .arg(cols.sexp())
        .arg(data)
        .eval_with_handlers(R_BaseEnv)
};

A column that does not exist raises tidyselect’s own error (class vctrs_error_subscript_oob, with its call), which unwinds through the Rust frames and reaches the user’s tryCatch() as it was raised. To get the error back as Err(REvalError) instead, while the user’s handlers still see warnings and messages, use try_eval_with_handlers.

§Safety

As eval_with_handlers: R’s main thread, inside a miniextendr boundary, env an environment. The result is unprotected.

Source

pub unsafe fn try_eval_with_handlers( &self, env: SEXP, ) -> Result<SEXP, REvalError>

Evaluate the call in env in the caller’s R context, keeping R’s condition handlers, with an R error returned as Err: see try_eval_with_handlers.

Use it for a call that forwards the user’s arguments to an R function and raises the function’s errors as the package’s own: the user’s withCallingHandlers(), suppressWarnings() and tryCatch(warning = ) see the function’s warnings and messages, and an R error comes back as REvalError. A [ method of a data-frame subclass, say:

ⓘ
let out = unsafe {
    RCall::new("[")
        .arg(frame)
        .arg(i)
        .named_arg("drop", drop)
        .try_eval_with_handlers(R_BaseEnv)
}
.unwrap_or_else(|e| rust_error!(class = e.reraise_class("pkg_subset_error"), "{e}"));

x[1, drop = FALSE] warns that drop is ignored; the user’s suppressWarnings() silences it. A column that does not exist (“undefined columns selected”) comes back as Err and is raised again under pkg_subset_error.

§Safety

As try_eval_with_handlers: R’s main thread, inside a miniextendr boundary, env an environment. The value is unprotected.

Source

pub unsafe fn namespaced(pkg: &str, fun_name: &str) -> Result<Self, REvalError>

Start building a namespaced call: pkg::fun(args…).

Looks up pkg::fun_name in the base environment and uses the resolved function closure as the call target. This respects R’s namespace lookup rules (exported + non-exported via :: / :::).

This is the runtime counterpart of the lowered pkg::fn(args…) form in r!(pkg::fn(args…)).

§Safety

Must be called from the R main thread.

§Panics

Panics if pkg or fun_name contain a null byte.

§Errors

R’s error if the namespace lookup fails (package not installed, or no such function).

Auto Trait Implementations§

§

impl !Send for RCall

§

impl !Sync for RCall

§

impl Freeze for RCall

§

impl RefUnwindSafe for RCall

§

impl Unpin for RCall

§

impl UnsafeUnpin for RCall

§

impl UnwindSafe for RCall

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> SizeHint for T
where T: ?Sized,

Source§

default fn lower_bound(&self) -> usize

🔬This is a nightly-only experimental API. (core_io_internals)
Returns a lower bound on the number of elements this container-like item contains. For example, an array [u8; 12] could return any value between 0 and 12 inclusively as a correct implementation. Read more
Source§

default fn upper_bound(&self) -> Option<usize>

🔬This is a nightly-only experimental API. (core_io_internals)
Returns an upper bound on the number of elements this container-like item contains if it can be determined, otherwise None. Read more
Source§

final fn size_hint(&self) -> (usize, Option<usize>)

🔬This is a nightly-only experimental API. (core_io_internals)
Returns an estimate for the number of elements this container like type contains. Read more
Source§

impl<T> SizedTypeProperties for T

Source§

#[doc(hidden)]
const SIZE: usize = _

🔬This is a nightly-only experimental API. (sized_type_properties)
Source§

#[doc(hidden)]
const ALIGN: usize = _

🔬This is a nightly-only experimental API. (sized_type_properties)
Source§

#[doc(hidden)]
const ALIGNMENT: Alignment = _

🔬This is a nightly-only experimental API. (ptr_alignment_type)
Source§

#[doc(hidden)]
const IS_ZST: bool = _

🔬This is a nightly-only experimental API. (sized_type_properties)
true if this type requires no storage. false if its size is greater than zero. Read more
Source§

#[doc(hidden)]
const LAYOUT: Layout = _

🔬This is a nightly-only experimental API. (sized_type_properties)
Source§

#[doc(hidden)]
const MAX_SLICE_LEN: usize = _

🔬This is a nightly-only experimental API. (sized_type_properties)
The largest safe length for a [Self]. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.

Layout§

Note: Most layout information is completely unstable and may even differ between compilations. The only exception is types with certain repr(...) attributes. Please see the Rust Reference's “Type Layout” chapter for details on type layout guarantees.

Size: 112 bytes