Skip to main content

DataFrameRow

Derive Macro DataFrameRow 

Source
#[derive(DataFrameRow)]
{
    // Attributes available to this derive:
    #[dataframe]
}
Expand description

Derive DataFrameRow: generates a companion *DataFrame type with collection fields, plus IntoR / TryFromSexp / IntoDataFrame impls for seamless R data.frame conversion.

§Example

ⓘ
#[derive(DataFrameRow)]
struct Measurement {
    time: f64,
    value: f64,
}

// Generates MeasurementDataFrame { time: Vec<f64>, value: Vec<f64> }
// plus conversion impls

§Struct-level attributes

  • #[dataframe(name = "CustomDf")] — custom name for the generated DataFrame type
  • #[dataframe(align)] — pad shorter columns with NA to match longest
  • #[dataframe(tag = "my_tag")] — attach a tag attribute to the data.frame
  • #[dataframe(conflicts = "string")] — resolve conflicting column types as strings

§Field-level attributes

  • #[dataframe(skip)] — omit this field from the DataFrame
  • #[dataframe(rename = "col")] — custom column name
  • #[dataframe(as_list)] — keep collection as single list column (no expansion)
  • #[dataframe(expand)] / #[dataframe(unnest)] — expand collection into suffixed columns
  • #[dataframe(width = N)] — pin expansion width (shorter rows get NA)

§Requirements

Of each field type the derive asks only what turning that field into columns needs, and a missing impl is reported at the field:

  • a scalar or opaque column: its column converts to R (Vec<T>: IntoR; in an enum, Vec<Option<T>>);
  • [T; N], or width / expand over an owned Vec<T> / Box<[T]>: T’s columns convert to R; the elements are moved, so T needs no Clone or Copy. Over a borrowed &[T; N] / &[T], T must also be Clone;
  • a flattened struct or nested enum: #[derive(DataFrameRow)];
  • as_factor: UnitEnumFactor, which #[derive(DataFrameRow)] on a unit-only enum and #[derive(RFactor)] both provide;
  • as_list: IntoList;
  • skip: nothing.

Everything else is generated only where the field types allow it. The companion is Debug / Clone when every column type is. A struct companion iterates its rows when every skip field’s type is Default (they are rebuilt with Default::default()). With rayon, from_rows_par fills the columns in parallel when the rows and column values are Send, placeholder-filled struct columns are Default, and flattened / as_list values are Clone; otherwise it fills them sequentially.

§Public surface (which verbs to call)

Every capability the derive provides has a documented, trait-based (or std) verb — reach for these, not any incidental inherent plumbing:

  • Rows → R data.frame: rows.into_dataframe()? (owned, GC-rooted BuiltDataFrame) or rows.wrap_data_frame() (deferred IntoR wrapper); parallel variant rows.into_dataframe_par()?. From the IntoDataFrame / AsDataFrameExt traits (both re-exported from miniextendr_api::prelude).
  • R data.frame → rows: Vec::<Row>::from_dataframe(&df)? (parallel: Vec::<Row>::from_dataframe_par(&df)?), from the FromDataFrame trait — or the one-call Row::try_from_dataframe(sexp) reader on the row type.
  • Rows ↔ the pure-Rust columnar companion (<Row>DataFrame, Vec-columns, no R involved): the ColumnarFrame trait (in the prelude) — <Row>DataFrame::from_rows(rows) / from_rows_par(rows) (parallel build of the companion, which into_dataframe_par does not give you) and, for row-iterable companions, companion.into_rows(). Vec<Row>: Into<companion> and the companion’s IntoIterator are the equivalent std verbs.
  • Enum split representation: rows.into_dataframe_split() returns one data.frame per variant (only that variant’s columns — no NA fill) as a GC-rooted DataFrameShape — Bare for a single-variant enum, PerVariantList (an R named list) otherwise — from the IntoDataFrameSplit trait (in the prelude). Enum rows only; struct derives don’t partition.

The generated <Row>DataFrame / <Row>DataFrameIter types are intermediate column-oriented companions; you rarely name them directly.