Skip to main content

Preserved

Struct Preserved 

Source
pub struct Preserved {
    sexp: SEXP,
    _not_send: PhantomData<*mut ()>,
}
Expand description

An owned GC root for one R object: R_PreserveObject when made, R_ReleaseObject when dropped.

Hold one where a Rust value keeps an R object across allocations or past the .Call() that made it, such as a field of a struct that lives in an ExternalPtr and drops with its finalizer. The object stays reachable for as long as the handle lives, whatever the drop order. ProtectScope and OwnedProtect use R’s protect stack instead, which unwinds in order and ends with the call.

ⓘ
struct Fit {
    data: Preserved, // the data frame the model was fitted on
}

let fit = Fit { data: Preserved::new(df) };
// Any number of allocations and `.Call()`s later:
let n = fit.data.len();
  • Clone preserves the object again. Each handle releases its own entry, so clones drop in any order.
  • The handle dereferences to its SEXP, so SexpExt methods apply to it. A SEXP copied out of it (*handle, as_sexp) stays rooted only while the handle lives.
  • IntoR releases the root and hands the object to the caller: return the handle from a #[miniextendr] function to return the object.
  • !Send + !Sync: the precious list is R main-thread state. Make, clone and drop the handle on R’s main thread.

§Cost

R keeps the precious list as one linked list. Preserving conses a cell onto its head, an allocation. Releasing searches the list from the head, one step per object preserved after this one and not yet released. Starting R with the environment variable R_HASH_PRECIOUS set splits the list into 1069 hashed buckets.

That suits a few long-lived objects. For many roots, or roots released in no particular order, use a ProtectPool: it preserves one list and inserts and releases in O(1), and it roots every ExternalPtr, because a Vec of handles dropped front to back makes each release scan the whole precious list. RefCountedArena does the same with a count per object. Within one call, the protect stack (ProtectScope) allocates nothing.

Fields§

§sexp: SEXP§_not_send: PhantomData<*mut ()>

!Send + !Sync: preserving and releasing edit R’s precious list.

Implementations§

Source§

impl Preserved

Source

pub fn new(value: impl IntoR) -> Self

Convert value to an R object and preserve it.

A SEXP converts to itself, so Preserved::new(sexp) roots an existing object. Rooting is immediate: nothing allocates between the conversion and R_PreserveObject, which keeps its argument reachable while it allocates its cell, so a fresh, unprotected object is safe to pass. Call it on R’s main thread.

Source

pub fn as_sexp(&self) -> SEXP

The preserved object, rooted for as long as this handle lives.

Source

fn release(self) -> SEXP

Release the root and return the object, now unprotected: the caller takes it over. R_ReleaseObject does not allocate, so returning the object straight to R (the .Call() return) is safe.

Methods from Deref<Target = SEXP>§

Source

pub fn is_null_ptr(self) -> bool

Is this SEXP a C null pointer (address 0x0)?

This answers a pointer question, not an R question. An SEXP that R hands you (a .Call() argument, a list element, an attribute) is never a C null pointer: R’s NULL is the R_NilValue singleton, a real object at a non-zero address. A C null pointer only shows up where C code uses address zero for “nothing here”: an uninitialised static or field (SEXP::null()), an ALTREP data1/data2 slot that was never set, or an FFI function that returns NULL on failure.

To ask “is this R’s NULL?”, use SEXP::is_nil().

use miniextendr_api::SEXP;

let unset = SEXP::null();
assert!(unset.is_null_ptr());

There is no SEXP::is_null(): the name read like a test for R’s NULL, so it is gone on purpose.

ⓘ
use miniextendr_api::SEXP;

let unset = SEXP::null();
assert!(unset.is_null());

See also: SEXP::is_nil(), SEXP::is_null_or_nil(), SEXP::null()

Source

pub fn is_nil(self) -> bool

Is this SEXP R’s NULL (R_NilValue)?

This is the check for an R value: an optional argument that defaults to NULL, an absent attribute, an empty list slot. It compares the address against the R_NilValue singleton and never dereferences self, so it is safe on a stale SEXP. A C null pointer is not R’s NULL; for that, use SEXP::is_null_ptr().

use miniextendr_api::SEXP;

// `x` is an argument whose R default is `NULL`: `f()` and `f(NULL)`
// both arrive as `R_NilValue`.
fn given(x: SEXP) -> bool {
    !x.is_nil()
}

See also: SEXP::nil(), SEXP::is_null_ptr(), SEXP::is_null_or_nil()

Source

pub fn is_null_or_nil(self) -> bool

Is this SEXP either a C null pointer or R’s NULL?

For a slot that can hold either kind of “nothing”: unset (address zero) or set to R_NilValue, such as an external pointer’s protected value. For an R value alone, SEXP::is_nil() is the right check.

use miniextendr_api::SEXP;

// A slot that was never set counts as empty.
assert!(SEXP::null().is_null_or_nil());

See also: SEXP::is_nil(), SEXP::is_null_ptr()

Source

pub fn as_ptr(self) -> *mut SEXPREC

Get the raw pointer.

Source

pub unsafe fn altrep_data1_raw(self) -> SEXP

Get the raw SEXP in the ALTREP data1 slot.

§Safety
  • self must be a valid ALTREP SEXP
  • Must be called from the R main thread
Source

pub unsafe fn altrep_data1_raw_unchecked(self) -> SEXP

Get the raw SEXP in the ALTREP data1 slot (unchecked — no thread routing).

§Safety
  • self must be a valid ALTREP SEXP
  • Must be called from the R main thread
Source

pub unsafe fn set_altrep_data1(self, v: SEXP)

Set the ALTREP data1 slot.

§Safety
  • self must be a valid ALTREP SEXP
  • Must be called from the R main thread
Source

pub unsafe fn altrep_data2_raw(self) -> SEXP

Get the raw SEXP in the ALTREP data2 slot.

§Safety
  • self must be a valid ALTREP SEXP
  • Must be called from the R main thread
Source

pub unsafe fn altrep_data2_raw_unchecked(self) -> SEXP

Get the raw SEXP in the ALTREP data2 slot (unchecked — no thread routing).

§Safety
  • self must be a valid ALTREP SEXP
  • Must be called from the R main thread
Source

pub unsafe fn set_altrep_data2(self, v: SEXP)

Set the ALTREP data2 slot.

§Safety
  • self must be a valid ALTREP SEXP
  • Must be called from the R main thread
Source

pub unsafe fn set_altrep_data2_unchecked(self, v: SEXP)

Set the ALTREP data2 slot (unchecked — no thread routing).

§Safety
  • self must be a valid ALTREP SEXP
  • Must be called from the R main thread

Trait Implementations§

Source§

impl Clone for Preserved

Source§

fn clone(&self) -> Self

Preserve the object again, for a handle with its own entry.

1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Preserved

Source§

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

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

impl Deref for Preserved

Source§

type Target = SEXP

The resulting type after dereferencing.
Source§

fn deref(&self) -> &SEXP

Dereferences the value.
Source§

impl Drop for Preserved

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 Preserved

Source§

type Error = !

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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> ConsumingFallible for T
where T: Clone,

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> RClone for T
where T: Clone,

Source§

fn r_clone(&self) -> T

Create a deep copy of this value.
Source§

#[doc(hidden)]
unsafe extern "C" fn __RClone_r_clone_shim( data: *mut c_void, argc: i32, argv: *const SEXP, ) -> SEXP
where Self: Sized + RClone + IntoR,

Method shim for RClone::r_clone. Read more
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§

#[doc(hidden)]
unsafe extern "C" fn __RDebug_debug_str_shim( data: *mut c_void, argc: i32, argv: *const SEXP, ) -> SEXP
where Self: Sized + RDebug,

Method shim for RDebug::debug_str. Read more
Source§

#[doc(hidden)]
unsafe extern "C" fn __RDebug_debug_str_pretty_shim( data: *mut c_void, argc: i32, argv: *const SEXP, ) -> SEXP
where Self: Sized + RDebug,

Method shim for RDebug::debug_str_pretty. Read more
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> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

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

Source§

type Error = !

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

fn try_from(value: U) -> Result<T, !>

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