🔗Prerequisites

  • Rust (1.85+): Install from rustup.rs
  • R (4.0+): Install from CRAN
  • R development tools: install.packages("devtools")

Verify your setup:

rustc --version   # Should be 1.85+
R --version       # Should be 4.0+

🔗Step 1: Create a New Package

Use the minirextendr helper package to scaffold a new project:

# Install minirextendr (once)
install.packages("minirextendr")

# Create a new package
library(minirextendr)
create_miniextendr_package("mypackage")

This creates:

mypackage/
├── DESCRIPTION
├── NAMESPACE
├── R/
│   └── mypackage_wrappers.R    # Auto-generated R wrappers
├── src/
│   └── rust/
│       ├── Cargo.toml
│       ├── lib.rs              # Your Rust code goes here
│       └── vendor/             # Vendored miniextendr crates
├── configure                   # Build configuration script
└── configure.ac                # Autoconf source

🔗Step 2: Write Your First Function

Edit src/rust/lib.rs:

use miniextendr_api::miniextendr;

/// Add two integers.
/// @param a First number
/// @param b Second number
/// @return The sum of a and b
#[miniextendr]
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

/// Greet someone by name.
/// @param name The name to greet
/// @return A greeting string
#[miniextendr]
pub fn greet(name: &str) -> String {
    format!("Hello, {}!", name)
}
// Registration is automatic via #[miniextendr].

🔗Step 3: Build and Test

# Recommended: devtools handles everything in one step
devtools::document("mypackage")
devtools::install("mypackage")

Or manually:

cd mypackage
./configure            # Generate build files
R CMD INSTALL .        # Compile Rust and install

🔗Step 4: Use from R

library(mypackage)

add(1L, 2L)
# [1] 3

greet("World")
# [1] "Hello, World!"

🔗The #[miniextendr] Attribute

Mark functions for export to R:

#[miniextendr]
pub fn my_function(x: i32) -> i32 {
    x * 2
}

The macro:

  • Generates a C wrapper callable from R
  • Handles type conversion (R <-> Rust)
  • Manages error handling and panics
  • Extracts documentation from Rust doc comments

Items annotated with #[miniextendr] are automatically registered via linkme distributed slices – no manual module declarations needed.

🔗Type Conversions

miniextendr automatically converts between R and Rust types:

R TypeRust Type
integeri32
numericf64
characterString, &str
logicalbool
integer vectorVec<i32>, &[i32]
numeric vectorVec<f64>, &[f64]
listVarious
NULL()
NAOption<T> (None = NA)

🔗Creating Classes

#[derive(miniextendr_api::ExternalPtr)]
pub struct Counter { value: i32 }

#[miniextendr]  // Default: env style
impl Counter {
    pub fn new(initial: i32) -> Self {
        Counter { value: initial }
    }

    pub fn value(&self) -> i32 {
        self.value
    }

    pub fn increment(&mut self) {
        self.value += 1;
    }
}
c <- Counter$new(0L)
c$value()      # 0
c$increment()
c$value()      # 1

Switch class system with a single attribute change:

#[miniextendr(r6)]   // R6 class
#[miniextendr(s3)]   // S3 generic functions
#[miniextendr(s4)]   // S4 setClass/setMethod
#[miniextendr(s7)]   // S7 new_class

🔗Error Handling

Rust panics are converted to R errors:

#[miniextendr]
pub fn divide(a: f64, b: f64) -> f64 {
    if b == 0.0 {
        panic!("Division by zero");
    }
    a / b
}
divide(1, 0)
# Error: Division by zero

Return Result<T, E> for structured error handling:

#[miniextendr]
pub fn parse_int(s: &str) -> Result<i32, String> {
    s.parse().map_err(|e| format!("Parse error: {}", e))
}

🔗Development Workflow

  1. Edit Rust code in src/rust/lib.rs
  2. Run devtools::document() – compiles Rust, generates R wrappers, runs roxygen2
  3. Run devtools::install() – install the package
  4. Test in R

🔗Next Steps