一键导入
miniextendr-classes
Use when exposing a Rust struct as an R class in a miniextendr package — choosing between R6, S3, S4, S7, Env, or vctrs; writing
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when exposing a Rust struct as an R class in a miniextendr package — choosing between R6, S3, S4, S7, Env, or vctrs; writing
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Use when the user asks how to expose a Rust struct as an R class, which R class system to use (R6 vs S3 vs S4 vs S7 vs Env vs Vctrs), how method dispatch works, how constructors are generated, how trait methods map to R, or when working in miniextendr-macros/src/miniextendr_impl/*.rs or miniextendr-macros/src/r_class_formatter.rs.
Use when the user asks about converting between R and Rust types, how TryFromSexp or IntoR work, what NA handling looks like, how strict mode differs from normal coercion, why Vec<i32> from an empty R vector panics, or how bool, Option<T>, or large integer types behave across the R-Rust boundary.
Use when the user asks about ExternalPtr, how Rust structs are stored as R objects, TypedExternal, Box<Box<dyn Any>> storage, pointer provenance for cached_ptr, the release_any finalizer, sidecar field accessors, the TYPE_NAME_CSTR vs TYPE_ID_CSTR distinction, or how to pass an ExternalPtr across crate or package boundaries.
Use when a new user asks how to start a miniextendr-backed R package from zero, how to scaffold via minirextendr::use_miniextendr(), what their first Rust function should look like, what the dev loop is, or "can I add Rust to an existing R package". Also use when someone is confused about which package is which (miniextendr vs minirextendr).
Use when the user asks how
Use when debugging configure.ac failures, Makevars issues, Cargo.lock mismatches, vendor tarball problems, build mode confusion, or the cdylib-to-staticlib double-link pipeline. Also use when changing a Makevars value, diagnosing a latch-leak, understanding the bash-vs-sh configure requirement, or working with m4 quoting in configure.ac.
| name | miniextendr-classes |
| description | Use when exposing a Rust struct as an R class in a miniextendr package — choosing between R6, S3, S4, S7, Env, or vctrs; writing |
Annotate an impl block with #[miniextendr(...)] and the framework
generates the complete R class — constructor, methods, documentation — backed
by your Rust struct held as an external pointer. You write Rust; the R side
is generated into R/<pkg>-wrappers.R on every build.
| System | Attribute | R usage | Reach for it when |
|---|---|---|---|
| Env | #[miniextendr] | Type$new(...), obj$method() | simplest wrapper, zero R dependencies |
| R6 | #[miniextendr(r6)] | Type$new(...), obj$method() | reference semantics, active bindings, private methods, chaining |
| S3 | #[miniextendr(s3)] | new_type(...), generic(obj) | idiomatic R generics, broad ecosystem interop |
| S4 | #[miniextendr(s4)] | Type(...), setMethod dispatch | formal/multiple dispatch, S4-heavy codebases |
| S7 | #[miniextendr(s7)] | Type(...), method(obj) | modern OOP, declared properties, inheritance |
| vctrs | #[miniextendr(vctrs)] | new_type(...) vectors | tidyverse-compatible vector/record types |
Default to R6 for stateful objects and S3 for lightweight
generic-style APIs. R6/S7/vctrs add the corresponding R package to your
DESCRIPTION — minirextendr::use_r6() / use_s7() / use_vctrs() /
use_s4() set that up.
use miniextendr_api::miniextendr;
pub struct Counter {
value: i32,
}
#[miniextendr(r6)]
impl Counter {
/// Create a new counter.
pub fn new(start: i32) -> Self {
Counter { value: start }
}
/// Increment and return self for chaining.
pub fn increment(&mut self) {
self.value += 1;
}
pub fn get(&self) -> i32 {
self.value
}
}
After minirextendr::miniextendr_build():
c <- Counter$new(10L)
c$increment()$increment() # void Rust methods return invisible(self) → chainable
c$get()
#> [1] 12
| Rust receiver | Becomes |
|---|---|
&self | instance method (read-only) |
&mut self | instance method (mutating; R6 returns invisible(self) for () returns) |
no receiver, returns Self | constructor (new) or static factory |
| no receiver, other return | static method: Type$method() / Type_method() |
Multiple impl blocks for one type need labels:
#[miniextendr(r6, label = "extra")] — otherwise the build errors on
duplicate class definitions.
panic! in new() becomes a proper R
error. The generated constructors validate the low-level result before
wrapping it — if you hand-write an R constructor around a .Call, you lose
that guard and a panicking constructor silently yields a corrupt object.
Prefer the generated constructors.@importFrom vctrs ... lines in
NAMESPACE force the vctrs DLL to load first. If vctrs dispatch fails,
check those lines survived your last document() run./// doc comments with @param/@return flow
into man/ per method.obj$method() works via
a generated $ S3 method that binds self. No dependencies; not a formal
class system (no inheritance).#[miniextendr(r6(private))] for private methods,
#[miniextendr(r6(prop = "name"))] for active bindings (computed
properties), r6(finalize) for a GC destructor, r6(deep_clone) for
custom clone logic.new_<type>() (lowercase); each instance method
gets a generic with a guard so it won't clobber a generic already defined
by another package.ptr slot; generics/methods are
registered via methods::setGeneric/setMethod. Don't name Rust methods
s4_* — the generator prefixes s4_ itself (the MXL111 lint catches the
resulting s4_s4_*).#[miniextendr(s7(getter))] /
s7(setter) / s7(validator) methods; multiple dispatch via
s7(dispatch = "x,y").vctrs::new_vctr() / new_rcrd() /
new_list_of(). Constructors must return SEXP (not Self) and the impl
block cannot have instance-method receivers — the build errors otherwise.Annotate the trait and each impl:
#[miniextendr]
pub trait Describe {
fn describe(&self) -> String;
}
#[miniextendr]
impl Describe for Counter {
fn describe(&self) -> String {
format!("Counter at {}", self.value)
}
}
R gets dual calling forms: Counter$Describe$describe(obj) and
obj$Describe$describe(). This is how you share one method surface across
several Rust types.
#[miniextendr(internal)] — exported but @keywords internal (hidden from
the docs index).#[miniextendr(noexport)] — not exported at all. Don't combine with
internal (redundant; the MXL203 lint flags it).R/<pkg>-wrappers.R — regenerated on every build; all edits
lost. Change the Rust and rebuild.minirextendr::miniextendr_build(), which also refreshes
NAMESPACE and reinstalls if exports changed.obj2 <- obj aliases the same Rust state in every class system
(external-pointer semantics), even in "value-semantics" systems like S4/S7.
Provide an explicit clone/copy method if callers need independent copies.Full manual (class systems chapter): https://a2-ai.github.io/miniextendr