Expand description
Runtime support for trait ABI operations.
Provides C-callable loading and type conversion helpers for trait ABI support.
§Trait ABI Runtime Support
This module provides runtime support for cross-package trait dispatch. It bridges between R’s external pointer system and Rust’s trait objects using a stable C ABI.
§Overview
The trait ABI system enables:
-
Cross-package dispatch: Package A can call trait methods on objects created by Package B, without compile-time knowledge of the concrete type.
-
Type safety: Runtime type checking via
mx_tagensures safe downcasts. -
Memory safety: R’s garbage collector manages object lifetime via external pointer finalizers.
§Architecture
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ R Code │ │ C-callables │ │ Rust Runtime │
│ │ │ (rpkg) │ │ (miniextendr) │
│ .Call("method", │────►│ mx_query() │────►│ vtable lookup │
│ obj, ...) │ │ mx_wrap() │ │ method shim │
│ │◄────│ mx_get() │◄────│ type conversion │
└─────────────────┘ └──────────────────┘ └─────────────────┘§Submodules
§Integration with ExternalPtr / TypedExternal
Trait ABI support is integrated with the ExternalPtr and TypedExternal
system. ExternalPtr<T> serves as the “traitless” case (equivalent to Any
in dynamic typing). Trait dispatch wrappers are automatically registered
when you annotate impl Trait for Type with #[miniextendr]:
#[derive(ExternalPtr)]
struct Circle { radius: f64 }
#[miniextendr]
impl Shape for Circle { /* ... */ }This generates the wrapper structures, vtable references, and query implementations for cross-package trait dispatch.
§Exporting traits you don’t own
You cannot apply #[miniextendr] to external traits. Instead, define a
local adapter trait that exposes the subset you want in R, then provide
a blanket impl for any type that implements the external trait:
use num_traits::Num;
#[miniextendr]
pub trait RNum {
fn add(&self, other: &Self) -> Self;
fn to_string(&self) -> String;
}
impl<T: Num + Clone + ToString> RNum for T {
fn add(&self, other: &Self) -> Self { self.clone() + other.clone() }
fn to_string(&self) -> String { ToString::to_string(self) }
}This keeps the ABI stable while avoiding generics in the trait itself.
§Initialization
Each package includes mx_abi.rs (from miniextendr-api) which provides the
mx_wrap/mx_get/mx_query functions. package_init() (via miniextendr_init!)
calls mx_abi_register() to initialize the tag symbol and register C-callables.
§Thread Safety
All trait ABI operations are main-thread only:
- R invokes
.Callon the main thread - Method shims do not route through
with_r_thread
§Example Usage
§Defining a trait (provider package)
// In package "shapes"
#[miniextendr]
pub trait Shape {
fn area(&self) -> f64;
fn perimeter(&self) -> f64;
}
#[derive(ExternalPtr)]
pub struct Circle { radius: f64 }
#[miniextendr]
impl Shape for Circle {
fn area(&self) -> f64 { std::f64::consts::PI * self.radius * self.radius }
fn perimeter(&self) -> f64 { 2.0 * std::f64::consts::PI * self.radius }
}§Using across packages (consumer package)
// In package "geometry" (depends on "shapes")
use shapes::{TAG_SHAPE, ShapeView};
fn calculate_area(obj: SEXP) -> f64 {
unsafe {
let view = mx_query_as::<ShapeView>(obj, TAG_SHAPE)
.expect("object does not implement Shape");
// Call method through vtable
view.area()
}
}Re-exports§
pub use conv::check_arity;pub use conv::extract_arg;pub use conv::from_sexp;pub use conv::nil;pub use conv::rf_error;pub use conv::to_sexp;pub use conv::try_from_sexp;
Modules§
Traits§
- Trait
View - Trait for view types that can be created from SEXP via trait dispatch.