원클릭으로
miniextendr-dataframe
Use when moving data.frames between R and Rust in a miniextendr package —
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Use when moving data.frames between R and Rust in a miniextendr package —
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Use when the user asks how to expose a Rust struct as an R class, which R class system to use (R6 vs S3 vs S4 vs S7 vs Env vs Vctrs), how method dispatch works, how constructors are generated, how trait methods map to R, or when working in miniextendr-macros/src/miniextendr_impl/*.rs or miniextendr-macros/src/r_class_formatter.rs.
Use when the user asks about converting between R and Rust types, how TryFromSexp or IntoR work, what NA handling looks like, how strict mode differs from normal coercion, why Vec<i32> from an empty R vector panics, or how bool, Option<T>, or large integer types behave across the R-Rust boundary.
Use when the user asks about ExternalPtr, how Rust structs are stored as R objects, TypedExternal, Box<Box<dyn Any>> storage, pointer provenance for cached_ptr, the release_any finalizer, sidecar field accessors, the TYPE_NAME_CSTR vs TYPE_ID_CSTR distinction, or how to pass an ExternalPtr across crate or package boundaries.
Use when a new user asks how to start a miniextendr-backed R package from zero, how to scaffold via minirextendr::use_miniextendr(), what their first Rust function should look like, what the dev loop is, or "can I add Rust to an existing R package". Also use when someone is confused about which package is which (miniextendr vs minirextendr).
Use when the user asks how
Use when debugging configure.ac failures, Makevars issues, Cargo.lock mismatches, vendor tarball problems, build mode confusion, or the cdylib-to-staticlib double-link pipeline. Also use when changing a Makevars value, diagnosing a latch-leak, understanding the bash-vs-sh configure requirement, or working with m4 quoting in configure.ac.
| name | miniextendr-dataframe |
| description | Use when moving data.frames between R and Rust in a miniextendr package — |
miniextendr has one owned DataFrame type and two traits that mirror the
scalar conversion surface:
| Trait | Call | Direction |
|---|---|---|
IntoDataFrame | rows.into_dataframe()? | Vec<Row> → R data.frame |
FromDataFrame | Vec::<Row>::from_dataframe(&df)? | R data.frame → Vec<Row> |
Both verbs live on the data itself. All errors are one type,
DataFrameError. Missing cells round-trip as Option<T> fields — that is
the entire NA contract.
use miniextendr_api::dataframe::{BuiltDataFrame, DataFrame, FromDataFrame, IntoDataFrame};
use miniextendr_api::{miniextendr, DataFrameRow, IntoList};
#[derive(Clone, IntoList, DataFrameRow)]
pub struct Point {
pub x: f64,
pub y: f64,
pub label: String,
pub weight: Option<f64>, // NA-able column
}
// Take an R-supplied frame as the cheap `DataFrame` view; return the freshly
// built frame as `BuiltDataFrame` (an owned, GC-rooted handle — the return type
// of every Rust-side constructor like `into_dataframe`). It Derefs to
// `DataFrame` and converts back to R automatically.
#[miniextendr]
pub fn shift_points(df: DataFrame, dx: f64) -> BuiltDataFrame {
let mut rows = Vec::<Point>::from_dataframe(&df).unwrap();
for p in &mut rows {
p.x += dx;
}
rows.into_dataframe().unwrap()
}
df <- data.frame(x = 1:3, y = 4:6, label = c("a","b","c"), weight = c(1, NA, 3))
shift_points(df, 10)
DataFrame implements the ordinary conversion traits, so it appears directly
in #[miniextendr] signatures. It wraps a validated data.frame SEXP —
handing it a list that isn't a data.frame errors cleanly.
f64, i32, String, bool, …) → one column each;
Option<T> for NA-able columns.[T; N] and Vec<T> + #[dataframe(width = N)] →
expanded to field_1 … field_N columns (ragged Vec pads trailing NA,
and round-trips losslessly).DataFrameRow → flattened with a
<field>_ prefix, arbitrarily deep.Vec<scalar> / Box<[scalar]> → an opaque list-column
(one R list element per row).HashMap/BTreeMap (scalar keys/values) → paired
<field>_keys / <field>_values list-columns, zipped back on read.#[dataframe(skip)] → field not written (and therefore not readable
back — from_dataframe on such a shape returns an error, not garbage).Tagged enums work as row types too: give the enum
#[dataframe(tag = "kind")] and each variant's fields become columns, with
the tag column dispatching per-row on the way back in. Unit-only nested enums
can render as factors with #[dataframe(as_factor)].
A few shapes are write-only (reading back returns a clear DataFrameError):
borrowed fields (&str/&[T]), skipped fields, opaque non-scalar
collections, and tagless enums. If from_dataframe errors on your type,
check the field shapes first.
For heterogeneous frames built column-wise, use the builder:
let df = DataFrame::builder(nrow)
.column::<f64>("x", |chunk, offset| {
for (i, slot) in chunk.iter_mut().enumerate() {
*slot = (offset + i) as f64;
}
})
.column_str("label", |i| Some(format!("row{i}")))
.build();
The builder fills serially by default and in parallel when the crate is built
with the rayon feature — same API either way.
rayon feature)_par variants produce identical results, just faster on large data:
let df = rows.into_dataframe_par()?;
let rows = Vec::<Point>::from_dataframe_par(&df)?;
Row structs are plain Rust data (no R pointers), so after extraction you can
process them with rayon freely: rows.par_iter().map(...). See the
miniextendr-parallel skill for the threading rules.
Types with serde::Serialize/Deserialize can skip DataFrameRow and
convert through the SerdeRows newtype:
use miniextendr_api::serde::SerdeRows;
let df = SerdeRows(rows).into_dataframe()?;
let rows = SerdeRows::<Row>::from_dataframe(&df)?.into_inner();
There are also free functions vec_to_dataframe(...) and
vec_to_dataframe_flatten_enums(...) (flattens nested enum fields into
columns) in miniextendr_api::serde. Prefer the DataFrameRow derive when
you control the type — it is checked at compile time and reads back; the
serde path shines for third-party types you can't annotate.
None columns land as logical NA columns (that's R's convention
for "unknown type, all missing"); R coerces them to the right type on first
combine. Mixed Some/None columns are unaffected.HashMap fields is non-deterministic (use BTreeMap
for byte-stable output); key→value pairing is always preserved.as.character() in R) or model it as
a unit enum + as_factor on the Rust side.DataFrame across long computations if you can extract
rows instead — rows are plain Rust data and are safe on any thread; the
DataFrame itself wraps R memory and must stay on the main thread.DataFrameError for diagnostics; it reports
the offending column and row where applicable.Full manual (DataFrame chapters): https://a2-ai.github.io/miniextendr