Guide
Getting Started
Create your first R package with a Rust backend
🔗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 Type | Rust Type |
|---|---|
| integer | i32 |
| numeric | f64 |
| character | String, &str |
| logical | bool |
| integer vector | Vec<i32>, &[i32] |
| numeric vector | Vec<f64>, &[f64] |
| list | Various |
| NULL | () |
| NA | Option<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
- Edit Rust code in
src/rust/lib.rs - Run
devtools::document()– compiles Rust, generates R wrappers, runs roxygen2 - Run
devtools::install()– install the package - Test in R
🔗Next Steps
- Architecture – How miniextendr works
- Type Conversions – Full type mapping reference
- Class Systems – Choosing and using R class systems
- ALTREP – Lazy and compact vectors