Skip to main content

Sidecar

Struct Sidecar 

Source
pub struct Sidecar<T> {
    pending: RefCell<Option<Pending>>,
    _value: PhantomData<T>,
}
Expand description

A sidecar field whose value R roots: it lives in the external pointer’s protection list, typed as T on the Rust side.

Declare it as #[r_data] pub keys: Sidecar<Vec<i32>> on a #[derive(ExternalPtr)] struct. The derive generates accessors as associated functions of the struct, with the field’s visibility. They take an owner handle, which keeps the pointer alive, not the struct:

#[r_data(...)]Rust accessors
reffn keys(ptr: &impl SidecarOwner<Self>) -> T
mutfn set_keys(ptr: &mut impl SidecarOwnerMut<Self>, value: T)
ref, mut, or a bare #[r_data]both

Rust code sees no SEXPs, slot indices or other R plumbing: a plain fn new() -> Self constructor fills the field with Sidecar::new, and a method that needs the value takes a view as its receiver (self: ExternalPtrRef<'_, Self> / mut self: ExternalPtrMut<'_, Self>) and calls Self::keys(&self) / Self::set_keys(&mut self, v); other Rust code writes Engine::keys(&ptr). The R side keeps its own accessors whatever the option: for a pub field, Type_get_keys(x) / Type_set_keys(x, value), an R6 active binding (r6(r_data_accessors)), an S7 property (s7(r_data_accessors)), or x$keys / x$keys <- value on an S3, S4 or env class (s3(r_data_accessors) & co.). The R setter validates the new value with TryFromSexp::<T> and stores the value R gave.

ⓘ
#[derive(ExternalPtr)]
#[externalptr(r6)]
pub struct Engine {
    n: i32,
    #[r_data] _r: RSidecar,
    #[r_data(ref, mut)] pub keys: Sidecar<Vec<i32>>,
    #[r_data(ref)] pub labels: Sidecar<Vec<String>>,
    #[r_data(ref, mut)] cache: Sidecar<Option<List>>,
}

#[miniextendr(r6(r_data_accessors))]
impl Engine {
    pub fn new(n: i32) -> Self {
        Engine {
            n,
            _r: RSidecar,
            keys: Sidecar::new((1..=n).collect()),
            labels: Sidecar::new(vec![]),
            cache: Sidecar::default(),
        }
    }
    pub fn key_count(self: ExternalPtrRef<'_, Self>) -> usize { Self::keys(&self).len() }
    pub fn remember(mut self: ExternalPtrMut<'_, Self>, value: List) {
        Self::set_cache(&mut self, Some(value))
    }
}

// From any other Rust code holding the handle:
let mut ptr = ExternalPtr::new(Engine::new(3));
Engine::set_keys(&mut ptr, vec![9]);
assert_eq!(Engine::keys(&ptr), vec![9]);

The accessors take super::ExternalPtrRef / super::ExternalPtrMut views (receivers, arguments, borrow() / borrow_mut()) or an ExternalPtr, which they borrow for the call: shared for the getter, exclusive for the setter. A plain &self receiver has no route back to an R owner.

§Values

  • T: IntoR + TryFromSexp, with T::Error: Display for the getter’s panic message.
  • A getter converts the slot with TryFromSexp, so a converting type (Vec<i32>, String) copies on every read. A conversion failure is a panic naming the field and the type; the framework turns it into an R error.
  • Sidecar<SEXP>, Sidecar<List> and Sidecar<DataFrame> keep an arbitrary R object unconverted. The getter returns a view that the slot roots only until the next write to that field; don’t hold it across one.
  • Sidecar::new and Sidecar::replace convert their value with IntoR at once and root the R object it gives until a slot holds it (#2216). A fresh SEXP or list passed to them is safe from that moment; it needs its own protection only before the call.

§Lifecycle

  • Pending. Sidecar::new(v) and Default (Sidecar::new(T::default())) convert v to its R object and hold it as a pending value in the struct, rooted in a main-thread pool until a slot holds it or the last copy drops. That is the only state a Sidecar has: before the struct is wrapped, after *ptr = Engine::new(..), after ExternalPtr::into_inner. They run on R’s main thread, or on the managed worker, which hands the conversion to the main thread as ExternalPtr::new does; any other thread panics. Like an owning ExternalPtr, the last copy of a pending value must be dropped on R’s main thread or its managed worker.
  • Flushed when wrapped. ExternalPtr::new and the trait ABI’s mx_wrap move every pending value into the pointer’s protection list. From then on the value lives in R only, and the field is empty.
  • Flushed on every access to the field. The handle getter and setter, and the R getter and setter, first move that field’s pending value into its slot (the setters drop it: the write supersedes it). So *ptr.borrow_mut() = Engine::new(..) or ptr.borrow_mut().keys = Sidecar::new(v) takes effect at the next access from either side. A saveRDS() before any access writes the old slot value.
  • Loaded when the value leaves its pointer. ExternalPtr::into_inner and into_raw read each slot of the pointer being consumed back into its field as a pending value (unless one is pending already), rooted as a new one is, so wrapping the value again keeps the values.
  • A self -> Self method leaves the values in the pointer across the call; the write-back flushes only what the method set with Sidecar::new. Self { n: .., ..self } keeps the values, Self::new(..) replaces them.
  • Clone. Sidecar::clone shares the pending R object and its root, which is none once the struct is wrapped. ExternalPtr::clone and clone_from copy the slot values as R objects, so the two pointers share them until one side writes the field: every write replaces the slot, and R copies a shared value before modifying it in place. A struct cloned outside its handle carries no sidecar values; clone the handle.
  • *ptr = new and mem::swap(&mut *a, &mut *b) are plain Rust moves. The sidecar values stay in each pointer’s protection list; only pending values travel with the struct.
  • After readRDS() there is no Rust value. The standalone R accessors (Type_get_f() / Type_set_f()), the R6 active bindings and the S7 properties read and write the protection list directly, checked by the type ID stored with it. The $ / [[ / $<- / [[<- field methods of s3(r_data_accessors) & co. refuse the restored pointer instead, with the classed restored-object errors: they pass require_live to the same accessor (#1891).

Use the accessors on R’s main thread only. Off the main thread an accessor panics with a clear message before it touches R.

§A save names the values by their R names

A saved object (saveRDS(), serialize()) stores one slot per Sidecar field, named after the field’s R name: the #[r_data(name = "..")] value, else the Rust field name (#2187, #2215). After a reload, the standalone R accessors, the R6 bindings, the S7 properties and a rebuild hook (#[externalptr(rebuild = path)], through super::Saved) find each field’s slot by that name. So the R name is the save format:

  • Reordering, adding or removing Sidecar fields keeps earlier saves readable. A field the save has no slot for raises the classed RESTORED_MISSING_SLOT_CLASS error (or reads as None through a hook’s Self::try_<field>(saved)).
  • Renaming the Rust field keeps them readable too, as long as the R name stays: a field without name = .. keeps its old ident as #[r_data(name = "old")].
  • Renaming the R name orphans the field’s slot in every earlier save: the field reads as one the save lacks.
  • Retyping a field keeps its name, so an earlier save’s slot is read as the new type; a getter that can’t convert it panics naming the field.

A save from another version of the crate (the version in its Cargo.toml, which the stored type ID records) is refused with the classed miniextendr_restored_other_version error (#1872), unless the type rebuilds it (rebuild = path, any_version).

§Methods that need sidecar values take a view

A &self / &mut self method sees the struct, which holds no reference to its pointer, so it can’t reach the values. Take a view instead: self: ExternalPtrRef<'_, Self> to read, mut self: ExternalPtrMut<'_, Self> to write (Deref still gives the struct fields). The same holds for a struct moved out of its pointer through &mut (mem::replace, mem::take): it carries nothing to follow, so nothing can read a freed R object (#1856). A trait method takes its owner as self: ExternalPtrRef<'_, Self> or self: ExternalPtrMut<'_, Self> (ExternalPtrRef), over an ExternalPtr or a trait-ABI object, and passes it to the same accessors. A &self trait method, such as one of a standard trait with a fixed signature, and the high-level ALTREP data traits receive only the struct (#1880). ALTREP derives also accept #[r_data] Sidecar<T> fields: with #[altrep(manual, no_lowlevel)], low-level callbacks recover the handle through crate::altrep_data1_as_unchecked and call the associated accessors. Those derives emit no R class field wrappers.

A plain SEXP field under #[r_data] is a compile error: nothing would root it. Use Sidecar<SEXP>.

Fields§

§pending: RefCell<Option<Pending>>

The value the field holds while it is not in a pointer’s slot.

§_value: PhantomData<T>

The field’s type: its accessors convert the slot to it, and the struct’s auto traits follow it as if it held a T.

Implementations§

Source§

impl<T: IntoR + 'static> Sidecar<T>

Source

pub fn new(value: T) -> Self

A sidecar field holding value, pending until the struct is wrapped in an ExternalPtr.

Converts value with IntoR and roots the R object until a slot holds it, so a fresh R value is safe from here on.

§Panics

Off R’s main thread and its managed worker, before converting; or when the conversion fails.

Source

pub fn replace(value: T) -> Self

A sidecar field holding value, which replaces the slot a save kept when a rebuild hook (#[externalptr(rebuild = path)]) returns it.

A rebuilt object keeps each slot its save has: the hook’s Sidecar::new values are placeholders for those fields, and fill only the fields the save lacks. A hook that recomputes a field’s value, and wants the object to hold the new value rather than the saved one, returns it as Sidecar::replace(value). Anywhere else it is Sidecar::new(value). Converts and roots value as Sidecar::new does, with the same panics.

Source

fn pending(value: T, replace: bool) -> Self

Converts value and roots its R object on R’s main thread: inline there, through the main thread from the managed worker (#2216).

Source§

impl<T> Sidecar<T>

Source

fn replaces_saved(&self) -> bool

Whether the pending value was made by Sidecar::replace.

Source

fn discard_pending(&self)

Drops a pending value, which a write to the slot supersedes.

Source

fn flush_into(&self, prot: SEXP, index: usize)

Moves a pending value into slot index of prot, if there is one. On R’s main thread; set_vector_elt allocates nothing.

Source

fn load_from(&self, prot: SEXP, index: usize)

Reads slot index of prot back into the field as its pending value, unless one is pending already. On R’s main thread.

Trait Implementations§

Source§

impl<T> Clone for Sidecar<T>

Source§

fn clone(&self) -> Self

A Sidecar sharing this field’s pending R object, and its root, if any. The value in a pointer’s slot is not copied: clone the ExternalPtr.

1.0.0 (const: unstable) · Source§

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

Performs copy-assignment from source. Read more
Source§

impl<T> Debug for Sidecar<T>

Source§

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

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

impl<T: Default + IntoR + 'static> Default for Sidecar<T>

Source§

fn default() -> Self

Sidecar::new(T::default()).

Source§

impl<T> SidecarField for Sidecar<T>

Source§

fn __mx_flush(&self, prot: SEXP, index: usize)

Moves a pending value into slot index of prot.
Source§

fn __mx_load(&self, prot: SEXP, index: usize)

Reads slot index of prot back into the field as its pending value, unless one is pending already.
Source§

fn __mx_discard_pending(&self)

Drops a pending value, which a write to the slot supersedes.
Source§

fn __mx_replaces_saved(&self) -> bool

Whether the pending value was made by Sidecar::replace.

Auto Trait Implementations§

§

impl<T> !Freeze for Sidecar<T>

§

impl<T> !RefUnwindSafe for Sidecar<T>

§

impl<T> !Sync for Sidecar<T>

§

impl<T> Send for Sidecar<T>
where PhantomData<T>: Send,

§

impl<T> Unpin for Sidecar<T>
where PhantomData<T>: Unpin,

§

impl<T> UnsafeUnpin for Sidecar<T>

§

impl<T> UnwindSafe for Sidecar<T>

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<T> RDefault for T
where T: Default,

Source§

fn r_default() -> T

Create a new instance with default values.
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: 32 bytes