#[miniextendr]Expand description
Export Rust items to R.
#[miniextendr] can be applied to:
fnitems (generate C + R wrappers)implblocks (generate R class methods)traititems (generate trait ABI metadata)- ALTREP wrapper structs (generate
RegisterAltrepimpls)
§Functions
use miniextendr_api::miniextendr;
#[miniextendr]
fn add(a: i32, b: i32) -> i32 { a + b }This produces a C wrapper C_add and an R wrapper add().
Registration is automatic via linkme distributed slices.
§extern "C-unwind"
If the function is declared extern "C-unwind" and exported with
#[no_mangle] (2021), #[unsafe(no_mangle)] (2024), or #[export_name = "..."],
the function itself is the C symbol and the R wrapper is prefixed with
unsafe_ to signal bypassed safety (no worker isolation or conversion).
§Variadics (...)
Use ... as the last argument. The Rust parameter becomes _dots: &Dots.
Use name @ ... to give it a custom name (e.g., args @ ... → args: &Dots).
§Typed Dots Validation
Use #[miniextendr(dots = typed_list!(...))] to automatically validate dots
and create a dots_typed variable with typed accessors:
#[miniextendr(dots = typed_list!(x => numeric(), y => integer(), z? => character()))]
pub fn my_func(...) -> String {
let x: f64 = dots_typed.get("x").expect("x");
let y: i32 = dots_typed.get("y").expect("y");
let z: Option<String> = dots_typed.get_opt("z").expect("z");
format!("x={}, y={}", x, y)
}Type specs: numeric(), integer(), logical(), character(), list(),
raw(), complex(), or "class_name" for class inheritance checks.
Add (n) for exact length: numeric(4). Use ? suffix for optional fields.
Use @exact; prefix for strict mode (reject extra fields).
§Attributes
#[miniextendr(unsafe(main_thread))]— run on R’s main thread (bypass worker)#[miniextendr(invisible)]/#[miniextendr(visible)]— control return visibility#[miniextendr(check_interrupt)]— check for user interrupt after call#[miniextendr(coerce)]— coerce R type before conversion (also usable per-parameter)#[miniextendr(strict)]— reject lossy conversions for i64/u64/isize/usize#[miniextendr(unwrap_in_r)]— returnResult<T, E>to R without unwrapping#[miniextendr(dots = typed_list!(...))]— validate dots, createdots_typed#[miniextendr(internal)]— adds@keywords internalto R wrapper#[miniextendr(noexport)]— suppresses@exportfrom R wrapper
§Impl blocks (class systems)
Apply #[miniextendr(env|r6|s7|s3|s4)] to an impl Type block.
Use #[miniextendr(label = "...")] to disambiguate multiple impl blocks
on the same type.
Registration is automatic.
§R6 Active Bindings
For R6 classes, use #[miniextendr(r6(active))] on methods to create
active bindings (computed properties accessed without parentheses):
use miniextendr_api::miniextendr;
pub struct Rectangle {
width: f64,
height: f64,
}
#[miniextendr(r6)]
impl Rectangle {
pub fn new(width: f64, height: f64) -> Self {
Self { width, height }
}
/// Returns the area (width * height).
#[miniextendr(r6(active))]
pub fn area(&self) -> f64 {
self.width * self.height
}
/// Regular method (requires parentheses).
pub fn scale(&mut self, factor: f64) {
self.width *= factor;
self.height *= factor;
}
}In R:
r <- Rectangle$new(3, 4)
r$area # 12 (active binding - no parentheses!)
r$scale(2) # Regular method call
r$area # 24Active bindings must be getter-only methods taking only &self.
§S7 Properties
For S7 classes, use #[miniextendr(s7(getter))] and #[miniextendr(s7(setter))]
to create computed properties accessed via @:
use miniextendr_api::{miniextendr, ExternalPtr};
#[derive(ExternalPtr)]
pub struct Range {
start: f64,
end: f64,
}
#[miniextendr(s7)]
impl Range {
pub fn new(start: f64, end: f64) -> Self {
Self { start, end }
}
/// Computed property (read-only): length of the range.
#[miniextendr(s7(getter))]
pub fn length(&self) -> f64 {
self.end - self.start
}
/// Dynamic property getter.
#[miniextendr(s7(getter, prop = "midpoint"))]
pub fn get_midpoint(&self) -> f64 {
(self.start + self.end) / 2.0
}
/// Dynamic property setter.
#[miniextendr(s7(setter, prop = "midpoint"))]
pub fn set_midpoint(&mut self, value: f64) {
let half = self.length() / 2.0;
self.start = value - half;
self.end = value + half;
}
}In R:
r <- Range(0, 10)
r@length # 10 (computed, read-only)
r@midpoint # 5 (dynamic property)
r@midpoint <- 20 # Adjusts start/end to center at 20§Property Attributes
#[miniextendr(s7(getter))]- Read-only computed property#[miniextendr(s7(getter, prop = "name"))]- Named property getter#[miniextendr(s7(setter, prop = "name"))]- Named property setter#[miniextendr(s7(getter, default = "0.0"))]- Property with default value#[miniextendr(s7(getter, required))]- Required property (error if not provided)#[miniextendr(s7(getter, frozen))]- Property that can only be set once#[miniextendr(s7(getter, deprecated = "Use X instead"))]- Deprecated property#[miniextendr(s7(validate))]- Validator function for property
§S7 Generic Dispatch Control
Control how S7 generics are created:
#[miniextendr(s7(no_dots))]- Create strict generic without...#[miniextendr(s7(dispatch = "x,y"))]- Multi-dispatch on multiple arguments#[miniextendr(s7(fallback))]- Register method forclass_any(catch-all). The generated R wrapper usestryCatch(x@.ptr, error = function(e) x)to safely extract the self argument, so non-miniextendr objects won’t crash with a slot-access error. Instead, incompatible objects produce a Rust type-conversion error when the method tries to interpret the argument as&Self.
#[miniextendr(s7)]
impl MyClass {
/// Strict generic: function(x) instead of function(x, ...)
#[miniextendr(s7(no_dots))]
pub fn strict_method(&self) -> i32 { 42 }
/// Fallback method dispatched on class_any.
/// Calling this on a non-MyClass object produces a type-conversion error,
/// not a slot-access crash.
#[miniextendr(s7(fallback))]
pub fn describe(&self) -> String { "generic description".into() }
}§S7 Type Conversion (convert)
Use convert_from and convert_to to enable S7’s convert() for type coercion:
use miniextendr_api::{miniextendr, ExternalPtr};
#[derive(ExternalPtr)]
pub struct Celsius { value: f64 }
#[derive(ExternalPtr)]
pub struct Fahrenheit { value: f64 }
#[miniextendr(s7)]
impl Fahrenheit {
pub fn new(value: f64) -> Self { Self { value } }
/// Convert FROM Celsius TO Fahrenheit.
/// Usage: S7::convert(celsius_obj, Fahrenheit)
#[miniextendr(s7(convert_from = "Celsius"))]
pub fn from_celsius(c: ExternalPtr<Celsius>) -> Self {
Fahrenheit { value: c.value * 9.0 / 5.0 + 32.0 }
}
/// Convert FROM Fahrenheit TO Celsius.
/// Usage: S7::convert(fahrenheit_obj, Celsius)
#[miniextendr(s7(convert_to = "Celsius"))]
pub fn to_celsius(&self) -> Celsius {
Celsius { value: (self.value - 32.0) * 5.0 / 9.0 }
}
}In R:
c <- Celsius(100)
f <- S7::convert(c, Fahrenheit) # Uses convert_from
c2 <- S7::convert(f, Celsius) # Uses convert_toNote: Classes must be defined before they can be referenced in convert methods. Define the “from” class before the “to” class to avoid forward reference issues.
§Traits (ABI)
Apply #[miniextendr] to a trait to generate ABI metadata, then use
#[miniextendr] impl Trait for Type. Registration is automatic.
§ALTREP
Apply #[miniextendr(class = "...", base = "...")] to a one-field
wrapper struct. Registration is automatic.