Expand description
Unevaluated-argument markers (Quoted / Quosure) for #[miniextendr] parameters.
Unevaluated arguments: the Quoted and Quosure parameter markers.
A generated R wrapper forces every argument it hands to .Call(), so a
Rust body normally sees values only. A parameter typed Quoted or
Quosure is passed unevaluated instead: the body receives the argument’s
expression and the environment to evaluate it in, and decides what to do
with them. That is what subset()-style methods and APIs taking bare
column names (f(data, TIME) as well as f(data, "TIME")) need.
| marker | the wrapper passes | needs | {{ x }} / !!x |
|---|---|---|---|
Quoted | list(substitute(x), parent.frame()) | base R | no |
Quosure | rlang::enquo(x) | rlang in Imports | yes |
use miniextendr_api::{miniextendr, Quoted, SEXP};
/// Rows of `data` where `cond` holds; `cond` sees the columns of `data`,
/// then the caller's variables.
#[miniextendr]
pub fn keep_rows(data: SEXP, cond: Quoted) -> Vec<i32> {
let keep = cond.eval_in(data);
// ... read the logical vector
}threshold <- 3
keep_rows(df, value > threshold)The wrapper never forces the argument: a Quoted / Quosure parameter
takes no R-side checks and none of the per-parameter options (coerce,
match_arg, choices, default, no_na, inherits, …), which are
compile errors on it. A function taking one runs on R’s main thread.
It works on free functions, including standalone S3 methods
(#[miniextendr(s3(generic = "subset", class = "..."))]): in a method
reached through UseMethod(), parent.frame() is the generic’s caller, as
in base R’s subset.data.frame().
A missing argument is a conversion error naming the parameter
(argument "cond" is missing, with no default), like forcing it in R. Take
Missing<Quoted> / Missing<Quosure> to make the argument optional.
Evaluation goes through crate::expression::eval_with_handlers: the
caller’s withCallingHandlers() / suppressWarnings() see what the
expression signals, and an error, or the exit of a tryCatch() handler set
up around the call, unwinds through the Rust frames (running their
destructors) and carries on in R as the original condition.
Both markers borrow from the .Call() arguments, which R keeps rooted for
the call; the lifetime keeps them from outliving it. They are !Send.
A Quosure can also be read from a quosure found in an R object
(Quosure::from_sexp), such as one a wrapper wrote into a call with
c(time = {{ t }}), or built from an expression and an environment
(Quosure::new, Quosure::args). It then borrows the root the caller
holds: the SEXP it was read from, or a ProtectScope.
Structs§
- Quosure
- An argument captured as an rlang quosure,
rlang::enquo(x): its expression and environment, with rlang’s argument forwarding and injection resolved. - Quoted
- An argument passed unevaluated: its expression and the environment it was
written in, as base R’s
substitute(x)andparent.frame()give them.
Functions§
- data_
mask 🔒 ⚠ - A new environment enclosed by
enclosthat binds each named element of the listdata, aseval()builds one for a listenvir. Bound in reverse so the first of two same-named elements wins, as ineval(). - expr_
name 🔒 - The name an argument expression stands for: a symbol’s name, or the value
of a length-1 character literal.
Nonefor anything else, and forNA. - innermost_
quosure 🔒 - The quosure
xstands for:xitself, or, when its expression is a quosure too (rlang::new_quosure(<quosure>)), the innermost one.Nonewhenxis not a quosure. - is_
quosure 🔒 - Whether
xis an rlang quosure: a one-sided formula,~expr, of classquosure. - r_index 🔒
- An R vector index.
- rlang_
new_ 🔒 ⚠quosure - The function
rlang::new_quosure, unprotected. - wrap_
in_ 🔒 ⚠quosure new_quosure(quote(<expr>), env), unprotected.