Skip to main content

BuiltDataFrame

Struct BuiltDataFrame 

Source
pub struct BuiltDataFrame {
    view: DataFrame,
    _not_send: PhantomData<*mut ()>,
}
Expand description

An owned, GC-rooted data.frame built on the Rust side.

DataFrame is a cheap Copy view over a bare SEXP with no root of its own — sound only while something else keeps the SEXP reachable (an R .Call argument frame roots R-supplied frames; a surrounding ProtectScope roots transients). A frame that Rust constructs has no such external root, so holding the bare view across any R allocation is a latent use-after-free (issue #1128): the GC reclaims the frame, and a freed-but-intact read passes tests silently until the slot is reused.

BuiltDataFrame is the return type of every Rust-side constructor (IntoDataFrame::into_dataframe, SerdeRowBuilder::finish, DataFrame::builder().build(), the serde *_to_dataframe verbs, NamedList::as_data_frame). It roots the frame with R_PreserveObject on construction and releases it with R_ReleaseObject on drop, so holding it across allocations is safe by construction. It Derefs to DataFrame, so every read/edit method keeps working unchanged; hand the frame back to R with IntoR::into_sexp — or just return it from a #[miniextendr] fn, which converts it through IntoR transparently.

Not Copy/Clone (each root is released exactly once) and not Send (R’s precious list is R-main-thread state).

§Editing (chains stay rooted, #1247)

The new-frame editing methods (DataFrame::drop, DataFrame::select, DataFrame::select_rows, DataFrame::prepend_column, DataFrame::with_column) also return BuiltDataFrame, and this type carries inherent forwards for the whole editing set, so rows.into_dataframe()?.drop("x").select(&["y"]) is rooted at every link. (The drop forward is load-bearing: without it, method resolution on a BuiltDataFrame receiver finds Drop::drop — E0040.)

§Residual

The cheap view can still be smuggled across allocations by hand: dereferencing (*built) copies out an unrooted DataFrame view that dangles once the handle drops. That is an opt-in footgun, not the constructor/editing path this type makes safe.

Fields§

§view: DataFrame§_not_send: PhantomData<*mut ()>

!Send + !Sync: rooting/unrooting mutates R’s global precious list, which is R-main-thread state. Construction and drop only ever happen inside framework code already on the R thread.

Implementations§

Source§

impl BuiltDataFrame

Source

pub unsafe fn adopt_sexp(sexp: SEXP) -> Self

Root an already-built data.frame SEXP and take ownership of the root.

§Safety

Must run on the R main thread. sexp must be a well-formed data.frame VECSXP. Rooting is immediate — no allocation happens between entry and R_PreserveObject, and R_PreserveObject protects sexp while it allocates its cons cell — so a freshly-assembled unprotected SEXP is safe to pass directly.

Source

pub unsafe fn adopt(df: DataFrame) -> Self

Root a DataFrame view, taking ownership of a fresh root.

Rooting an already-rooted frame is sound: R_PreserveObject entries stack (the precious list is a cons list, duplicates allowed), and each BuiltDataFrame releases exactly the one entry it added.

§Safety

Must run on the R main thread. df must wrap a well-formed, still-live data.frame VECSXP (reachable at the moment of the call).

Source

fn release_into_sexp(self) -> SEXP

Detach the rooted SEXP, releasing the Rust-side root without running Drop. Shared by the IntoR hand-off methods.

Mirrors DataFrame::as_sexp / IntoR::into_sexp: the returned SEXP is unprotected and the caller (typically the .Call return path) takes over protection. Releasing the precious-list root then returning matches that existing hand-off contract — R_ReleaseObject does not allocate, so no GC can run between the release and the return. into_sexp is exposed only through IntoR (like DataFrame), so there is no inherent method to shadow the trait’s.

Source

pub fn drop(self, col: &str) -> BuiltDataFrame

Remove a column by name — see DataFrame::drop.

Inherent forward: consumes this handle and returns the rooted result. (Also shadows Drop::drop, which method resolution would otherwise select — E0040.)

Source

pub fn select(self, cols: &[&str]) -> BuiltDataFrame

Keep only the named columns — see DataFrame::select.

Source

pub fn select_rows(&self, idx: &[usize]) -> BuiltDataFrame

Keep only the given rows — see DataFrame::select_rows.

Source

pub fn prepend_column(self, name: &str, column: SEXP) -> BuiltDataFrame

Insert a column at index 0 — see DataFrame::prepend_column.

column must be kept reachable by the caller across this call.

Source

pub fn with_column(self, name: &str, column: SEXP) -> BuiltDataFrame

Upsert a column — see DataFrame::with_column.

column must be kept reachable by the caller across this call.

Source

pub fn rename(self, from: &str, to: &str) -> BuiltDataFrame

Rename a column — see DataFrame::rename.

In-place edit of the same frame: this handle (and its root) carries straight through.

Source

pub fn strip_prefix(self, prefix: &str) -> BuiltDataFrame

Strip a prefix from column names — see DataFrame::strip_prefix.

In-place edit of the same frame: this handle (and its root) carries straight through.

Methods from Deref<Target = DataFrame>§

Source

pub fn group_by(&self, col: &str) -> Result<GroupedDataFrame, DataFrameError>

Partition this frame’s rows by the values of the named column.

Computes group indices in a single pass on the main thread. Supported key columns: factor (fast path — levels are the keys, level order kept, empty levels included), character, integer, and logical. Double columns error — cut() or factor() the column in R first.

NA keys form one group, ordered last (unlike R split(), which drops NA-keyed rows). See the module docs for the full key semantics.

Source

pub fn group_by_multi( &self, cols: &[&str], ) -> Result<GroupedDataFrame, DataFrameError>

Partition this frame’s rows by a composite key over several columns — the multi-column analogue of group_by.

Each supported column contributes one scalar key per row (factor level, character, integer, or logical — same rules and errors as group_by); the per-row keys are zipped into a GroupKey::Tuple in column order.

§Order

Non-NA groups match split(df, interaction(col1, col2, …, drop = TRUE)): the first column varies fastest (R interaction()’s default, lex.order = FALSE), each column ordered as group_by would order it alone (factor level order, byte-sorted characters, numeric integers, FALSE then TRUE). For character keys the match is exact for keys whose byte order coincides with the session’s collation — always true in the C locale; single-case ASCII in practice (e.g. en_US.UTF-8 collates a A b B where byte order gives A B a b). This is inherited from group_by’s byte-order choice for character keys — see the group-order note in the module docs.

§NA

interaction() maps any row with an NA in any component to NA and split() drops it. This method instead keeps such rows: every distinct NA-containing tuple forms its own group, all ordered after the non-NA groups, in first-encounter row order. This extends the single-column NA-last convention (see the module docs).

§Slice

An empty slice is an error. A single-column slice delegates to group_by and yields scalar keys (not 1-tuples), so callers never have to special-case one-element tuples.

Source

pub fn group_by_metadata(&self) -> Result<GroupedDataFrame, DataFrameError>

Ingest a dplyr grouped_df’s existing grouping from its groups attribute — honoring the caller’s grouping without recomputing it.

dplyr stores a grouped_df’s grouping in attr(df, "groups"): a data.frame whose leading columns are the group-key columns (one row per group, in dplyr’s group order) and whose trailing .rows list-column holds, per group, the 1-based row indices into df. This method reads that metadata verbatim into a GroupedDataFrame — the same type group_by / group_by_multi produce — so a #[miniextendr] function handed a dplyr-grouped frame can respect the caller’s grouping, including multi-column groupings.

Unlike group_by, this does no recomputation: a plain (non-grouped) data.frame is an error (NotGroupedDataFrame). Callers who want the framework to compute grouping should use group_by / group_by_multi.

§Keys

A single key column yields scalar GroupKeys; multiple key columns yield GroupKey::Tuples (labels .-joined), consistent with group_by_multi. Supported key-column types are the same as group_by (factor, character, integer, logical); a double / list key column is an error.

§Order & empty groups

The groups-frame row order is preserved verbatim — dplyr’s order is authoritative, with no re-sorting and no NA reordering. .drop = FALSE empty groups (zero-length .rows) are kept as groups with empty index vectors, mirroring the empty-factor-level convention of group_by.

§Errors

NotGroupedDataFrame (no groups attribute / not a data.frame), MissingGroupRows (no .rows column), BadGroupRows (a .rows element is not an integer/integerish vector), or GroupIndexOutOfRange (a .rows index is < 1 or > nrow). Every .rows index is converted from R’s 1-based to 0-based.

§GC

The groups attribute frame (and its columns) is only read here, during construction; it stays reachable via self’s attribute pairlist — which R protects as a .Call argument frame for the duration of the call — so it needs no separate root. Only the returned GroupedDataFrame roots the source frame for its own lifetime (see its GC-rooting docs).

Source

pub fn column<T>(&self, name: &str) -> Option<T>
where T: TryFromSexp,

Get a column by name, converting to type T.

Returns None if the column name is not found or conversion fails.

T may be a vector/collection target (Vec<f64>, Vec<i32>, Vec<String>, …) — the natural shape for a column — or a scalar for a length-1 column. The conversion error is discarded (that is what makes this return Option), so T::Error is unconstrained; use column_raw when you need the error.

Source

pub fn column_index<T>(&self, idx: usize) -> Option<T>
where T: TryFromSexp,

Get a column by 0-based index, converting to type T.

As with column, T may be a vector/collection or a scalar target type; the conversion error is discarded.

Source

pub fn column_raw(&self, name: &str) -> Option<SEXP>

Get the raw SEXP for a column by name.

Source

pub fn nrow(&self) -> usize

Number of rows.

Source

pub fn ncol(&self) -> usize

Number of columns.

Source

pub fn names(&self) -> Vec<String>

Collect column names in column order.

Source

pub fn contains_column(&self, name: &str) -> bool

Check whether a column name exists.

Source

pub fn validate( &self, spec: &TypedListSpec, ) -> Result<TypedList, TypedListError>

Validate the data frame’s column types against a TypedListSpec.

Source

pub fn as_list(&self) -> List

Get the underlying List.

Source

pub fn as_sexp(&self) -> SEXP

Get the underlying SEXP.

Source

fn named_list(&self) -> NamedList

Build the NamedList index for O(1) column-by-name access.

Source

pub fn select_rows(&self, idx: &[usize]) -> BuiltDataFrame

Keep only the rows at the given 0-based indices, in order.

Subsets every column (each a vector or list-column) to the specified rows and rebuilds compact integer row.names. Used by the enum reader to densify a flattened sub-frame before recursing into the inner type’s reader.

§PROTECT discipline

Allocates one new column vector per column — OwnedProtects the output list across the loop so previously-built column SEXPs survive subsequent allocations.

§Rooting

Returns an owned, GC-rooted BuiltDataFrame — see drop’s rooting note (#1247).

Trait Implementations§

Source§

impl Debug for BuiltDataFrame

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Deref for BuiltDataFrame

Source§

type Target = DataFrame

The resulting type after dereferencing.
Source§

fn deref(&self) -> &DataFrame

Dereferences the value.
Source§

impl Drop for BuiltDataFrame

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more
Source§

impl IntoR for BuiltDataFrame

Source§

type Error = Infallible

The error type for fallible conversions. Read more
Source§

fn try_into_sexp(self) -> Result<SEXP, Self::Error>

Try to convert this value to an R SEXP. Read more
Source§

unsafe fn try_into_sexp_unchecked(self) -> Result<SEXP, Self::Error>

Try to convert to SEXP without thread safety checks. Read more
Source§

fn into_sexp(self) -> SEXP

Convert this value to an R SEXP, panicking on error. Read more
Source§

unsafe fn into_sexp_unchecked(self) -> SEXP
where Self: Sized,

Convert to SEXP without thread safety checks, panicking on error. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> RDebug for T
where T: Debug,

Source§

fn debug_str(&self) -> String

Get a compact debug string representation.
Source§

fn debug_str_pretty(&self) -> String

Get a pretty-printed debug string with indentation.
Source§

impl<P, T> Receiver for P
where P: Deref<Target = T> + ?Sized, T: ?Sized,

Source§

type Target = T

🔬This is a nightly-only experimental API. (arbitrary_self_types)
The target type on which the method may be called.
Source§

impl<T> SizeHint for T
where T: ?Sized,

Source§

default fn lower_bound(&self) -> usize

🔬This is a nightly-only experimental API. (core_io_internals)
Returns a lower bound on the number of elements this container-like item contains. For example, an array [u8; 12] could return any value between 0 and 12 inclusively as a correct implementation. Read more
Source§

default fn upper_bound(&self) -> Option<usize>

🔬This is a nightly-only experimental API. (core_io_internals)
Returns an upper bound on the number of elements this container-like item contains if it can be determined, otherwise None. Read more
Source§

final fn size_hint(&self) -> (usize, Option<usize>)

🔬This is a nightly-only experimental API. (core_io_internals)
Returns an estimate for the number of elements this container like type contains. Read more
Source§

impl<T> SizedTypeProperties for T

Source§

#[doc(hidden)]
const SIZE: usize = _

🔬This is a nightly-only experimental API. (sized_type_properties)
Source§

#[doc(hidden)]
const ALIGN: usize = _

🔬This is a nightly-only experimental API. (sized_type_properties)
Source§

#[doc(hidden)]
const ALIGNMENT: Alignment = _

🔬This is a nightly-only experimental API. (ptr_alignment_type)
Source§

#[doc(hidden)]
const IS_ZST: bool = _

🔬This is a nightly-only experimental API. (sized_type_properties)
true if this type requires no storage. false if its size is greater than zero. Read more
Source§

#[doc(hidden)]
const LAYOUT: Layout = _

🔬This is a nightly-only experimental API. (sized_type_properties)
Source§

#[doc(hidden)]
const MAX_SLICE_LEN: usize = _

🔬This is a nightly-only experimental API. (sized_type_properties)
The largest safe length for a [Self]. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.

Layout§

Note: Most layout information is completely unstable and may even differ between compilations. The only exception is types with certain repr(...) attributes. Please see the Rust Reference's “Type Layout” chapter for details on type layout guarantees.

Size: 8 bytes