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
impl BuiltDataFrame
Sourcepub unsafe fn adopt_sexp(sexp: SEXP) -> Self
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.
Sourcepub unsafe fn adopt(df: DataFrame) -> Self
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).
Sourcefn release_into_sexp(self) -> SEXP
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.
Sourcepub fn drop(self, col: &str) -> BuiltDataFrame
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.)
Sourcepub fn select(self, cols: &[&str]) -> BuiltDataFrame
pub fn select(self, cols: &[&str]) -> BuiltDataFrame
Keep only the named columns — see DataFrame::select.
Sourcepub fn select_rows(&self, idx: &[usize]) -> BuiltDataFrame
pub fn select_rows(&self, idx: &[usize]) -> BuiltDataFrame
Keep only the given rows — see DataFrame::select_rows.
Sourcepub fn prepend_column(self, name: &str, column: SEXP) -> BuiltDataFrame
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.
Sourcepub fn with_column(self, name: &str, column: SEXP) -> BuiltDataFrame
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.
Sourcepub fn rename(self, from: &str, to: &str) -> BuiltDataFrame
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.
Sourcepub fn strip_prefix(self, prefix: &str) -> BuiltDataFrame
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>§
Sourcepub fn group_by(&self, col: &str) -> Result<GroupedDataFrame, DataFrameError>
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.
Sourcepub fn group_by_multi(
&self,
cols: &[&str],
) -> Result<GroupedDataFrame, DataFrameError>
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.
Sourcepub fn group_by_metadata(&self) -> Result<GroupedDataFrame, DataFrameError>
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).
Sourcepub fn column<T>(&self, name: &str) -> Option<T>where
T: TryFromSexp,
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.
Sourcepub fn column_index<T>(&self, idx: usize) -> Option<T>where
T: TryFromSexp,
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.
Sourcepub fn column_raw(&self, name: &str) -> Option<SEXP>
pub fn column_raw(&self, name: &str) -> Option<SEXP>
Get the raw SEXP for a column by name.
Sourcepub fn contains_column(&self, name: &str) -> bool
pub fn contains_column(&self, name: &str) -> bool
Check whether a column name exists.
Sourcepub fn validate(
&self,
spec: &TypedListSpec,
) -> Result<TypedList, TypedListError>
pub fn validate( &self, spec: &TypedListSpec, ) -> Result<TypedList, TypedListError>
Validate the data frame’s column types against a TypedListSpec.
Sourcefn named_list(&self) -> NamedList
fn named_list(&self) -> NamedList
Build the NamedList index for O(1) column-by-name access.
Sourcepub fn select_rows(&self, idx: &[usize]) -> BuiltDataFrame
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
impl Debug for BuiltDataFrame
Source§impl Deref for BuiltDataFrame
impl Deref for BuiltDataFrame
Source§impl Drop for BuiltDataFrame
impl Drop for BuiltDataFrame
Source§impl IntoR for BuiltDataFrame
impl IntoR for BuiltDataFrame
Source§type Error = Infallible
type Error = Infallible
Source§fn try_into_sexp(self) -> Result<SEXP, Self::Error>
fn try_into_sexp(self) -> Result<SEXP, Self::Error>
Auto Trait Implementations§
impl !Send for BuiltDataFrame
impl !Sync for BuiltDataFrame
impl Freeze for BuiltDataFrame
impl RefUnwindSafe for BuiltDataFrame
impl Unpin for BuiltDataFrame
impl UnsafeUnpin for BuiltDataFrame
impl UnwindSafe for BuiltDataFrame
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> SizeHint for Twhere
T: ?Sized,
impl<T> SizeHint for Twhere
T: ?Sized,
Source§default fn lower_bound(&self) -> usize
default fn lower_bound(&self) -> usize
core_io_internals)[u8; 12] could return any value between 0 and
12 inclusively as a correct implementation. Read moreSource§impl<T> SizedTypeProperties for T
impl<T> SizedTypeProperties for T
Source§#[doc(hidden)]const SIZE: usize = _
#[doc(hidden)]const SIZE: usize = _
sized_type_properties)Source§#[doc(hidden)]const ALIGN: usize = _
#[doc(hidden)]const ALIGN: usize = _
sized_type_properties)Source§#[doc(hidden)]const ALIGNMENT: Alignment = _
#[doc(hidden)]const ALIGNMENT: Alignment = _
ptr_alignment_type)Source§#[doc(hidden)]const IS_ZST: bool = _
#[doc(hidden)]const IS_ZST: bool = _
sized_type_properties)Source§#[doc(hidden)]const LAYOUT: Layout = _
#[doc(hidden)]const LAYOUT: Layout = _
sized_type_properties)Source§#[doc(hidden)]const MAX_SLICE_LEN: usize = _
#[doc(hidden)]const MAX_SLICE_LEN: usize = _
sized_type_properties)[Self]. Read moreLayout§
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