Skip to main content

Module trait_abi

Module trait_abi 

Source
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:

  1. Cross-package dispatch: Package A can call trait methods on objects created by Package B, without compile-time knowledge of the concrete type.

  2. Type safety: Runtime type checking via mx_tag ensures safe downcasts.

  3. 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

  • ccall: Direct FFI wrappers for mx_abi.rs functions
  • conv: Type conversion helpers for method shims

§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 .Call on 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§

ccall
Direct FFI to mx_abi Functions
conv
Type Conversion Helpers for Method Shims

Traits§

TraitView
Trait for view types that can be created from SEXP via trait dispatch.