Skip to main content

Module precondition_marker

Module precondition_marker 

Source
Expand description

Precondition markers (Checked<T> / Unchecked<T>) for #[miniextendr] parameters. Precondition markers: Checked<T> and Unchecked<T>.

A generated R wrapper checks each argument’s R type before the .Call() (is.integer(n), length(n) == 1L, …), one guard per check, so a wrong argument fails in R with a message naming it. These are the type-derived checks: they come from the parameter’s Rust type. Without them the Rust conversion still refuses bad input, with the same argument-error condition (e$param, e$rust_type, kind = "conversion"); only the message is the conversion’s. Dropping them saves one isTRUE() guard per check on a hot path.

Whether a parameter keeps them is decided per parameter, the most specific spelling first:

rankspellingwhere
1x: Checked<T> / x: Unchecked<T>a parameter of a function or inherent-impl method
1#[miniextendr(preconditions)] / #[miniextendr(no_preconditions)] on the parametera function parameter
1preconditions(x, y) / no_preconditions(x)a method attribute (inherent and trait impls)
2preconditions / no_preconditionsthe function or method attribute
3preconditions / no_preconditionsthe impl-block attribute
4preconditions = true | false[package.metadata.miniextendr] in Cargo.toml
5the no-preconditions-default cargo featurethe whole build
6onthe framework default
ⓘ
use miniextendr_api::{Checked, List, Unchecked, miniextendr};

/// `n_iter` keeps its guards; the function's `no_preconditions` drops
/// those of `data` and `tol`.
#[miniextendr(no_preconditions)]
pub fn fit(data: List, n_iter: Checked<i32>, tol: f64) -> f64 {
    let n = *n_iter; // or n_iter.into_inner()
    // ...
}

/// `xs` skips `is.double(xs)`; `factor` keeps its guards.
#[miniextendr]
pub fn scale_by(factor: f64, xs: Unchecked<Vec<f64>>) -> Vec<f64> {
    xs.into_inner().into_iter().map(|x| x * factor).collect()
}

The switch covers the type-derived checks only. The checks an author names (inherits, no_na), the match_arg / choices validation and the Rust conversion stay whatever the spelling. A marker and a keyword that disagree on one parameter are a compile error; two keywords follow the pair rule, the last one written wins.

The marker goes outermost (Checked<Option<i32>>, not Option<Checked<i32>>), at most one per parameter, and only on a parameter that has a type-derived check to keep or drop (not SEXP, Missing<T>, ExternalPtr<T>, &Dots, a choice parameter, …). It cannot appear in a #[miniextendr] trait’s method signature (spell it on the impl, #[miniextendr(preconditions(x))]) nor on an extern "C-unwind" function, which has no generated conversion to unwrap it. A marker is a parameter type only: as a return type it is a compile error.

§Not a runtime type

The guards are R text the macro writes before any type is resolved, so only the syntax Checked<..> / Unchecked<..> on the parameter selects them. The markers therefore implement neither TryFromSexp nor IntoR: the macro converts the inner T and wraps it (from_inner) before the call. A type alias (type N = Checked<i32>) or a rename is not seen as a marker, and fails to compile (E0277, no TryFromSexp) instead of silently following the crate default. The return-visibility markers (Invisible<T>, Visible<T>) differ: they forward IntoR, because a missed visibility marker still has a correct value to convert.

§Name

Checked means “the R-side guards are kept”. It is not an overflow-checked conversion, and not the thread-checked FFI variants (#[r_ffi_checked]).

Macros§

precondition_marker_impls 🔒

Structs§

Checked
Parameter marker: keep this parameter’s type-derived R-side checks, whatever the function, impl or crate default says. Transparent over T; see the module docs.
Unchecked
Parameter marker: drop this parameter’s type-derived R-side checks, and let the Rust conversion word a bad argument. Transparent over T; see the module docs.