Reference page
Enums and Factors Guide
How to map Rust enums to R factors and character strings.
How to map Rust enums to R factors and character strings.
miniextendr provides two complementary systems for enum-like types:
| System | R Representation | Partial Match | Default | Use Case |
|---|---|---|---|---|
RFactor | Factor (integer + levels) | No | β | Categorical data for table(), lm(), etc. |
MatchArg | Character scalar | Yes | First choice | Parameter validation (match.arg() style) |
πRFactor β Enum as R Factor
Maps a Rust enum to an R factor with levels. Each variant becomes a level.
#[derive(Copy, Clone, RFactor)]
pub enum Color {
Red, // level index 1
Green, // level index 2
Blue, // level index 3
}
Use in functions:
#[miniextendr]
pub fn describe(color: Color) -> &'static str {
match color {
Color::Red => "warm",
Color::Green => "cool",
Color::Blue => "cool",
}
}
#[miniextendr]
pub fn favorite() -> Color {
Color::Blue
}
From R:
describe(factor("Red", levels = c("Red", "Green", "Blue")))
# [1] "warm"
favorite()
# [1] Blue
# Levels: Red Green BlueπRename Variants
#[derive(Copy, Clone, RFactor)]
#[r_factor(rename_all = "snake_case")]
pub enum Status {
InProgress, // level: "in_progress"
Completed, // level: "completed"
NotStarted, // level: "not_started"
}
#[derive(Copy, Clone, RFactor)]
pub enum Priority {
#[r_factor(rename = "lo")]
Low,
#[r_factor(rename = "med")]
Medium,
#[r_factor(rename = "hi")]
High,
}
Supported rename_all values: snake_case, kebab-case, lower, upper.
πFactor Vectors
Use FactorVec<T> for vectors and FactorOptionVec<T> for vectors with NA:
use miniextendr_api::{FactorVec, FactorOptionVec};
#[miniextendr]
pub fn all_colors() -> FactorVec<Color> {
FactorVec(vec![Color::Red, Color::Green, Color::Blue])
}
#[miniextendr]
pub fn parse_colors(input: FactorOptionVec<Color>) -> Vec<&'static str> {
input.0.iter().map(|c| match c {
Some(Color::Red) => "red",
Some(Color::Green) => "green",
Some(Color::Blue) => "blue",
None => "NA",
}).collect()
}
From R:
all_colors()
# [1] Red Green Blue
# Levels: Red Green Blue
x <- factor(c("Red", NA, "Blue"), levels = c("Red", "Green", "Blue"))
parse_colors(x)
# [1] "red" "NA" "blue"πCaching
The #[derive(RFactor)] macro generates a OnceLock-cached levels STRSXP. The
levels string vector is allocated once and reused for all subsequent conversions,
giving ~4x speedup for single-value conversions.
πVia #[miniextendr]
Instead of #[derive(RFactor)], you can use the attribute macro:
#[miniextendr]
#[derive(Copy, Clone)]
pub enum Color { Red, Green, Blue }
This is equivalent β #[miniextendr] on a fieldless enum dispatches to the same
RFactor derive internally.
πMatchArg β Enum as String Parameter
Maps a Rust enum to R character strings with match.arg() validation. Supports
partial matching and defaults to the first variant when NULL is passed.
#[derive(Copy, Clone, MatchArg)]
pub enum Mode {
Fast, // choice: "Fast"
Safe, // choice: "Safe"
Debug, // choice: "Debug"
}
Use in functions:
#[miniextendr]
pub fn run(mode: Mode) -> String {
match mode {
Mode::Fast => "running fast".into(),
Mode::Safe => "running safe".into(),
Mode::Debug => "running debug".into(),
}
}
The generated R wrapper includes match.arg() validation:
run <- function(mode = NULL) {
.__mx_choices_mode <- .Call(C_run__match_arg_choices__mode)
mode <- if (is.factor(mode)) as.character(mode) else mode
mode <- base::match.arg(mode, .__mx_choices_mode)
.Call(C_run, mode)
}
From R:
run("Fast") # exact match
run("F") # partial match β "Fast"
run() # NULL β default (first choice: "Fast")
run("Saf") # partial match β "Safe"
run("X") # Error: 'arg' should be one of "Fast", "Safe", "Debug"πRename Variants
Same syntax as RFactor but with #[match_arg(...)]:
#[derive(Copy, Clone, MatchArg)]
#[match_arg(rename_all = "snake_case")]
pub enum BuildStatus {
InProgress, // choice: "in_progress"
Completed, // choice: "completed"
}
#[derive(Copy, Clone, MatchArg)]
pub enum Priority {
#[match_arg(rename = "lo")] Low,
#[match_arg(rename = "med")] Medium,
#[match_arg(rename = "hi")] High,
}πVia #[miniextendr]
#[miniextendr(match_arg)]
#[derive(Copy, Clone)]
pub enum Mode { Fast, Safe, Debug }πInline String Choices
For simple cases where you donβt need an enum, use choices(...) on a &str parameter:
#[miniextendr]
pub fn correlate(
x: f64, y: f64,
#[miniextendr(choices("pearson", "kendall", "spearman"))] method: &str,
) -> String {
format!("method={}, cor={}", method, x * y)
}
πMatchArg as Base Trait
MatchArg is the base trait for all enum-like types. RFactor requires MatchArg
as a supertrait, so any RFactor type also has MatchArg::CHOICES, from_choice(),
and to_choice(). Use MatchArg as a bound for generic code over both systems:
use miniextendr_api::MatchArg;
fn describe_choices<T: MatchArg>() -> String {
T::CHOICES.join(", ")
}
fn lookup<T: MatchArg>(choice: &str) -> Option<T> {
T::from_choice(choice)
}
πComparison Table
| Feature | RFactor | MatchArg |
|---|---|---|
| R storage | factor(1, levels=c(...)) | "Fast" (character) |
| Validation | Type check (is factor with correct levels) | match.arg() with partial matching |
| Default on NULL | Error | First choice |
| Vec support | FactorVec<T>, FactorOptionVec<T> | Single values only |
| Partial matching | No | Yes ("F" β "Fast") |
| Factor input | Native | Converted to character first |
| Use case | Categorical data | Parameter selection |
πWhen to Use Which
RFactor when:
- Data is categorical (colors, species, status codes)
- Working with R functions expecting factors (
table(),lm(),ggplot2) - Need vector support with NA handling
- Factor level ordering matters
MatchArg when:
- Building an API with string-based options
- Want Rβs
match.arg()partial matching and error messages - Want a default value when the argument is omitted
- Validating user input parameters
πSee Also
- MINIEXTENDR_ATTRIBUTE.md β
#[miniextendr]on enums - TYPE_CONVERSIONS.md β Full type conversion reference