Skip to main content

Module miniextendr_trait

Module miniextendr_trait 

Source
Expand description

§Trait Support for #[miniextendr]

This module handles #[miniextendr] applied to trait definitions, generating the ABI infrastructure for cross-package trait dispatch.

§Overview

When #[miniextendr] is applied to a trait, it generates:

  1. Type tag constant (TAG_<TraitName>) - 128-bit identifier for runtime type checking
  2. Vtable struct (<TraitName>VTable) - Function pointer table for method dispatch
  3. View struct (<TraitName>View) - Runtime wrapper combining data pointer and vtable
  4. Method shims - extern "C" functions that convert SEXP arguments and call methods
  5. Vtable builder - __<trait>_build_vtable::<T>() for impl blocks

§Usage

ⓘ
#[miniextendr]
pub trait Counter {
    fn value(&self) -> i32;
    fn increment(&mut self);
    fn add(&mut self, n: i32);
}

Generates (conceptually):

// Original trait (passed through)
pub trait Counter {
    fn value(&self) -> i32;
    fn increment(&mut self);
    fn add(&mut self, n: i32);
}

// Type tag for runtime identification
pub const TAG_Counter: mx_tag = mx_tag::new(0x..., 0x...);

// Vtable with one entry per method
#[repr(C)]
pub struct CounterVTable {
    pub value: mx_meth,
    pub increment: mx_meth,
    pub add: mx_meth,
}

// View over the data and vtable pointers; roots its R receiver
pub struct CounterView {
    __mx_view: miniextendr_api::trait_abi::TraitViewData<CounterVTable>,
}

// Shim for each method
unsafe extern "C" fn __Counter_value_shim<T: Counter>(
    data: *mut c_void, argc: i32, argv: *const SEXP
) -> SEXP {
    // 1. Check arity
    // 2. Cast data to &T
    // 3. Call method
    // 4. Convert result to SEXP
    // 5. Catch panics
}

// Builder to create vtable for a concrete type
pub const fn __Counter_build_vtable<T: Counter>() -> CounterVTable {
    CounterVTable {
        value: __Counter_value_shim::<T>,
        increment: __Counter_increment_shim::<T>,
        add: __Counter_add_shim::<T>,
    }
}

§Supported Method Signatures

Methods must follow these constraints:

  • Receiver: &self, &mut self, self: ExternalPtrRef<'_, Self> or self: ExternalPtrMut<'_, Self> for instance methods, or none for static methods. Borrowed-owner receivers require Self: TypedExternal; their emitted Rust signatures are associated functions accepting the handle by value.
  • Arguments: Types that implement TryFromSexp
  • Return: Types that implement IntoR, or ()
  • No generics: Methods cannot have generic type parameters
  • No async: Async methods are not supported
  • Static methods: Methods without a receiver are allowed and resolved at compile time (they don’t go through the vtable)

§Default Methods

Default method implementations are supported. The vtable builder will use the default implementation if the concrete type doesn’t override it.

ⓘ
#[miniextendr]
pub trait Counter {
    fn value(&self) -> i32;

    // Default implementation - included in vtable
    fn is_zero(&self) -> bool {
        self.value() == 0
    }
}

§Error Handling

A method shim runs inside with_r_unwind_protect_shim, which turns a Rust panic into a tagged condition SEXP; the View re-panics with it in the caller’s guard. These panic:

  • Arity mismatch: “expected N arguments, got M”
  • Aliased native borrows: two arguments (or two elements of one) that borrow the same R vector, at least one mutably, checked before any argument is converted
  • Type conversion failure: the converter’s error message
  • The method itself

§GC Protection

A View method converts each argument to a fresh SEXP, and the next conversion allocates, so it protects every converted argument in a ProtectScope as soon as it exists and keeps it protected across the vtable call. The returned SEXP is protected across the tagged-error check and its conversion back to Rust.

§Thread Safety

All generated shims are main-thread only. They do not route through with_r_thread because R invokes .Call on the main thread.

Structs§

ExtraBounds 🔒
Extra trait bounds inferred from method signatures.
MethodInfo 🔒
Information extracted from a trait method for code generation.

Functions§

associated_method_shim 🔒
Move the declaration shim into the trait so an impl can name it through any path to that trait. Self: Sized keeps these static ABI callbacks out of Rust trait objects. Types in the body still resolve at the declaration.
build_where_predicates 🔒
Build combined where predicates from the trait’s own where clause and computed extra bounds.
compute_extra_bounds 🔒
Compute extra bounds needed for the shim and build_vtable functions.
expand_trait
Expand #[miniextendr] applied to a trait definition.
extract_method_info 🔒
Extract method information from a trait method definition.
generate_method_shim 🔒
Generate a method shim function for a trait method.
generate_trait_abi 🔒
Generate the ABI infrastructure for a trait.
generate_view_method 🔒
Generate a method wrapper for the View struct.
rewrite_self_in_type 🔒
Rewrite Self and Self::AssocType in a type tree to use __ImplT.
trait_abi_tag_path 🔒
Encode the exported interface, in vtable order, for mx_tag_from_path.
type_contains_ident 🔒
Check if a type syntactically contains a specific identifier.
type_contains_self 🔒
Check if a type syntactically contains Self.
type_contains_self_assoc 🔒
Check if a type syntactically contains Self::AssocType for a given associated type name.
validate_method 🔒
Validate a single trait method for ABI compatibility.
validate_trait 🔒
Validate that the trait meets requirements for ABI generation.