Skip to main content

ProtectScope

Struct ProtectScope 

Source
pub struct ProtectScope {
    n: Cell<i32>,
    armed: Cell<bool>,
    _nosend: PhantomData<Rc<()>>,
}
Expand description

A scope that automatically balances UNPROTECT(n) on drop.

This is the primary tool for managing GC protection in batch operations. Each call to protect or protect_with_index increments an internal counter; when the scope is dropped, UNPROTECT(n) is called.

§Example

unsafe fn my_call(x: SEXP, y: SEXP) -> SEXP {
    let scope = ProtectScope::new();
    let x = scope.protect(x);
    let y = scope.protect(y);

    // Both x and y are protected until scope drops
    let result = scope.protect(some_operation(x.get(), y.get()));
    result.get()
} // UNPROTECT(3)

§Nested Scopes

Scopes can be nested. Each scope tracks only its own protections:

unsafe fn outer(x: SEXP) -> SEXP {
    let scope = ProtectScope::new();
    let x = scope.protect(x);

    let result = helper(&scope, x.get());
    scope.protect(result).get()
} // UNPROTECT(2)

unsafe fn helper(_parent: &ProtectScope, x: SEXP) -> SEXP {
    let scope = ProtectScope::new();
    let temp = scope.protect(allocate_something());
    combine(x, temp.get())
} // UNPROTECT(1) - only this scope's protections

Fields§

§n: Cell<i32>§armed: Cell<bool>§_nosend: PhantomData<Rc<()>>

Implementations§

Source§

impl ProtectScope

Source

pub unsafe fn new() -> Self

Create a new protection scope.

§Safety

Must be called from the R main thread.

Source

pub unsafe fn protect<'a>(&'a self, x: SEXP) -> Root<'a>

Protect x and return a rooted handle tied to this scope.

This always calls Rf_protect. The protection is released when the scope is dropped (along with all other protections in this scope).

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

pub unsafe fn protect_raw(&self, x: SEXP) -> SEXP

Protect and return the raw SEXP (sometimes more convenient).

§Safety

Same as protect.

Source

pub unsafe fn protect_with_index<'a>(&'a self, x: SEXP) -> ReprotectSlot<'a>

Protect x with an index slot so it can be replaced later via R_Reprotect.

Use this when you need to update a protected value in-place without growing the protection stack.

§Safety
  • Must be called from the R main thread
  • x must be a valid SEXP
§Example
unsafe fn accumulate(values: &[SEXP]) -> SEXP {
    let scope = ProtectScope::new();
    let slot = scope.protect_with_index(values[0]);

    for &v in &values[1..] {
        let combined = combine(slot.get(), v);
        slot.set(combined);  // Reprotect without growing stack
    }

    slot.get()
}
Source

pub unsafe fn protect2<'a>(&'a self, a: SEXP, b: SEXP) -> (Root<'a>, Root<'a>)

Protect two values at once (convenience method).

§Safety

Same as protect.

Source

pub unsafe fn protect3<'a>( &'a self, a: SEXP, b: SEXP, c: SEXP, ) -> (Root<'a>, Root<'a>, Root<'a>)

Protect three values at once (convenience method).

§Safety

Same as protect.

Source

pub fn count(&self) -> i32

Return the current protection count.

Source

pub unsafe fn disarm(&self)

Escape hatch: disable UNPROTECT on drop.

After calling this, the scope will not unprotect its values when dropped. You become responsible for ensuring correct unprotection.

§Safety

You must ensure the protects performed in this scope are correctly unprotected elsewhere, or you will leak protect stack entries.

Source

pub unsafe fn rearm(&self)

Re-arm a previously disarmed scope.

§Safety

Only call if you know the scope was disarmed and you want to restore automatic unprotection. Be careful not to double-unprotect.

Source

pub unsafe fn alloc_vector<'a>(&'a self, ty: SEXPTYPE, n: R_xlen_t) -> Root<'a>

Allocate a vector of the given type and length, and immediately protect it.

This combines allocation and protection in a single step, eliminating the GC gap that exists when you separately allocate and then protect.

§Safety
  • Must be called from the R main thread
  • Only protects the newly allocated object; does not protect other live unprotected objects during allocation
§Example
unsafe fn make_ints(n: R_xlen_t) -> SEXP {
    let scope = ProtectScope::new();
    let vec = scope.alloc_vector(SEXPTYPE::INTSXP, n);
    // fill via INTEGER(vec.get()) ...
    vec.get()
}
Source

pub unsafe fn alloc_matrix<'a>( &'a self, ty: SEXPTYPE, nrow: i32, ncol: i32, ) -> Root<'a>

Allocate a matrix of the given type and dimensions, and immediately protect it.

§Safety

Same as alloc_vector.

Source

pub unsafe fn alloc_list<'a>(&'a self, n: i32) -> Root<'a>

Allocate a list (VECSXP) of the given length and immediately protect it.

§Safety

Same as alloc_vector.

Source

pub unsafe fn alloc_strsxp<'a>(&'a self, n: usize) -> Root<'a>

Allocate a STRSXP (character vector) of the given length and immediately protect it.

§Safety

Same as alloc_vector.

Source

pub unsafe fn alloc_vecsxp<'a>(&'a self, n: usize) -> Root<'a>

Allocate a VECSXP (generic list) of the given length and immediately protect it.

§Safety

Same as alloc_vector.

Source

pub unsafe fn alloc_vector_unchecked<'a>( &'a self, ty: SEXPTYPE, n: R_xlen_t, ) -> Root<'a>

Allocate a vector via the unchecked FFI path and immediately protect it.

_unchecked twin of alloc_vector: allocates with Rf_allocVector_unchecked (bypassing the main-thread assertion / worker round-trip) for use inside ALTREP callbacks, with_r_unwind_protect, or with_r_thread bodies. Protection still goes through the (checked) Rf_protect — matching the established OwnedProtect-in-unchecked-context idiom — and is released with the rest of the scope on drop.

§Safety

Must be called from the R main thread, and only from a context where the checked-FFI assertion is intentionally bypassed (see CLAUDE.md “FFI thread checking”).

Source

pub unsafe fn alloc_vecsxp_unchecked<'a>(&'a self, n: usize) -> Root<'a>

Allocate a VECSXP via the unchecked FFI path, protected.

_unchecked twin of alloc_vecsxp. See alloc_vector_unchecked for the safety contract.

§Safety

Same as alloc_vector_unchecked.

Source

pub unsafe fn alloc_character_unchecked<'a>(&'a self, n: usize) -> Root<'a>

Allocate a STRSXP via the unchecked FFI path, protected.

_unchecked twin of alloc_character. See alloc_vector_unchecked for the safety contract.

§Safety

Same as alloc_vector_unchecked.

Source

pub unsafe fn alloc_integer<'a>(&'a self, n: usize) -> Root<'a>

Allocate an integer vector (INTSXP), protected.

§Safety

Must be called from the R main thread.

Source

pub unsafe fn alloc_real<'a>(&'a self, n: usize) -> Root<'a>

Allocate a real vector (REALSXP), protected.

§Safety

Must be called from the R main thread.

Source

pub unsafe fn alloc_logical<'a>(&'a self, n: usize) -> Root<'a>

Allocate a logical vector (LGLSXP), protected.

§Safety

Must be called from the R main thread.

Source

pub unsafe fn alloc_raw<'a>(&'a self, n: usize) -> Root<'a>

Allocate a raw vector (RAWSXP), protected.

§Safety

Must be called from the R main thread.

Source

pub unsafe fn alloc_complex<'a>(&'a self, n: usize) -> Root<'a>

Allocate a complex vector (CPLXSXP), protected.

§Safety

Must be called from the R main thread.

Source

pub unsafe fn alloc_character<'a>(&'a self, n: usize) -> Root<'a>

Allocate a character vector (STRSXP), protected.

§Safety

Must be called from the R main thread.

Source

pub unsafe fn scalar_integer<'a>(&'a self, x: i32) -> Root<'a>

Create a scalar integer (length-1 INTSXP), protected.

§Safety

Must be called from the R main thread.

Source

pub unsafe fn scalar_real<'a>(&'a self, x: f64) -> Root<'a>

Create a scalar real (length-1 REALSXP), protected.

§Safety

Must be called from the R main thread.

Source

pub unsafe fn scalar_logical<'a>(&'a self, x: bool) -> Root<'a>

Create a scalar logical (length-1 LGLSXP), protected.

§Safety

Must be called from the R main thread.

Source

pub unsafe fn scalar_complex<'a>(&'a self, x: Rcomplex) -> Root<'a>

Create a scalar complex (length-1 CPLXSXP), protected.

§Safety

Must be called from the R main thread.

Source

pub unsafe fn scalar_raw<'a>(&'a self, x: u8) -> Root<'a>

Create a scalar raw (length-1 RAWSXP), protected.

§Safety

Must be called from the R main thread.

Source

pub unsafe fn scalar_string<'a>(&'a self, s: &str) -> Root<'a>

Create a scalar string (length-1 STRSXP) from a Rust &str, protected.

§Safety

Must be called from the R main thread.

Source

pub unsafe fn mkchar<'a>(&'a self, s: &str) -> Root<'a>

Create a CHARSXP from a Rust &str, protected.

§Safety

Must be called from the R main thread.

Source

pub unsafe fn duplicate<'a>(&'a self, x: SEXP) -> Root<'a>

Deep-duplicate a SEXP, protected.

§Safety

Must be called from the R main thread. x must be a valid SEXP.

Source

pub unsafe fn shallow_duplicate<'a>(&'a self, x: SEXP) -> Root<'a>

Shallow-duplicate a SEXP, protected.

§Safety

Must be called from the R main thread. x must be a valid SEXP.

Source

pub unsafe fn coerce<'a>(&'a self, x: SEXP, target: SEXPTYPE) -> Root<'a>

Coerce a SEXP to a different type, protected.

§Safety

Must be called from the R main thread. x must be a valid SEXP.

Source

pub unsafe fn new_env<'a>( &'a self, parent: SEXP, hash: bool, size: i32, ) -> Root<'a>

Create a new environment, protected.

§Safety

Must be called from the R main thread.

Source

pub unsafe fn alloc_array<'a>(&'a self, ty: SEXPTYPE, dims: &[i32]) -> Root<'a>

Allocate an n-dimensional array of the given type, and immediately protect it.

The dims slice is first allocated as an integer vector inside this scope (consuming one protect slot) to satisfy Rf_allocArray’s SEXP-dims contract. The resulting array SEXP consumes a second protect slot.

§Protect-stack budget

This helper consumes two protect slots — one for the dims vector and one for the array — yet returns a single Root (for the array). The dims INTSXP has no accessible handle: it stays protected until the scope drops. Consequently count() increases by 2 even though the caller sees only one Root. Callers budgeting protect-stack depth must account for both slots, not just the returned handle.

§Safety

Must be called from the R main thread. dims must be non-empty.

Source

pub unsafe fn alloc_3d_array<'a>( &'a self, ty: SEXPTYPE, nrow: i32, ncol: i32, nface: i32, ) -> Root<'a>

Allocate a 3-dimensional array of the given type, and immediately protect it.

§Safety

Must be called from the R main thread.

Source

pub unsafe fn alloc_lang<'a>(&'a self, n: i32) -> Root<'a>

Allocate a language object (LANGSXP) of the given length, and immediately protect it.

§Safety

Must be called from the R main thread.

Source

pub unsafe fn alloc_s4_object<'a>(&'a self) -> Root<'a>

Allocate an S4 object (S4SXP), and immediately protect it.

§Safety

Must be called from the R main thread.

Source

pub unsafe fn mkchar_len_ce<'a>(&'a self, s: &[u8], enc: cetype_t) -> Root<'a>

Create a CHARSXP with a specified encoding and byte length, protected.

This is the general form; see mkchar for the plain UTF-8 shorthand.

§Safety

Must be called from the R main thread. s must be valid for len bytes.

Source

pub unsafe fn mkchar_ce<'a>(&'a self, s: &str, enc: cetype_t) -> Root<'a>

Create a CHARSXP from a Rust &str with a specified encoding, protected.

This is the length-counted variant (wraps Rf_mkCharLenCE); it is safe for strings with embedded NUL bytes and does not require a NUL terminator. For the raw NUL-terminated Rf_mkCharCE form, use mkchar_len_ce with s.as_bytes().

§Safety

Must be called from the R main thread.

Source

pub unsafe fn cons<'a>(&'a self, car: SEXP, cdr: SEXP) -> Root<'a>

Construct a pairlist cons cell (LISTSXP) and immediately protect it.

Allocates a single cons cell with car as the head and cdr as the tail. Both car and cdr must already be protected.

§Safety

Must be called from the R main thread. car and cdr must be valid SEXPs.

Source

pub unsafe fn lcons<'a>(&'a self, car: SEXP, cdr: SEXP) -> Root<'a>

Construct a language cons cell (LANGSXP) and immediately protect it.

Like cons but marks the node as LANGSXP (function call list).

§Safety

Must be called from the R main thread. car and cdr must be valid SEXPs.

Source

pub unsafe fn lengthgets<'a>(&'a self, x: SEXP, n: R_xlen_t) -> Root<'a>

Resize a vector to a new length (short-vector variant), returning a protected copy.

Wraps Rf_lengthgets. Both the source vector and the resized copy should be protected; the copy is protected by this call.

§Safety

Must be called from the R main thread. x must be a valid vector SEXP.

Source

pub unsafe fn xlengthgets<'a>(&'a self, x: SEXP, n: R_xlen_t) -> Root<'a>

Resize a vector to a new length (long-vector variant), returning a protected copy.

Like lengthgets but uses Rf_xlengthgets, which accepts R_xlen_t lengths beyond INT_MAX.

§Safety

Must be called from the R main thread. x must be a valid vector SEXP.

Source

pub unsafe fn make_external_ptr<'a>( &'a self, p: *mut c_void, tag: SEXP, prot: SEXP, ) -> Root<'a>

Create an external pointer SEXP, and immediately protect it.

This is an escape-hatch tier wrapper around R_MakeExternalPtr. In most cases you should use ExternalPtr<T> instead — it provides typed safety, finalizer registration, and correct rooting (#841). Use this wrapper only when you need raw EXTPTRSXP construction that ExternalPtr<T> cannot express.

Both tag and prot must be valid SEXPs (pass R_NilValue if unused).

§Safety

Must be called from the R main thread. p, tag, and prot must all be valid for the duration of the external pointer’s lifetime.

Source

pub unsafe fn rooted<'a>(&'a self, sexp: SEXP) -> Root<'a>

Create a Root<'a> for an already-protected SEXP without adding protection.

This is useful when you have a SEXP that is already protected by some other mechanism (e.g., a ReprotectSlot) and want to return it as a Root tied to this scope’s lifetime for API consistency.

§Safety
  • The caller must ensure sexp is already protected and will remain protected for at least the lifetime of this scope
  • Must be called from the R main thread
Source

pub unsafe fn collect<'a, T, I>(&'a self, iter: I) -> Root<'a>
where T: RNativeType, I: IntoIterator<Item = T>, I::IntoIter: ExactSizeIterator,

Collect an iterator into a typed R vector.

This allocates once, protects, and fills directly - the most efficient pattern for typed vectors. The element type T determines the R vector type via the RNativeType trait.

§Type Mapping
Rust TypeR Vector Type
i32INTSXP
f64REALSXP
u8RAWSXP
RLogicalLGLSXP
RcomplexCPLXSXP
§Safety

Must be called from the R main thread.

§Example
unsafe fn squares(n: usize) -> SEXP {
    let scope = ProtectScope::new();
    // Type inferred from iterator
    scope.collect((0..n).map(|i| (i * i) as i32)).get()
}
§Unknown Length

For iterators without exact size (e.g., filter), collect to Vec first:

let evens: Vec<i32> = data.iter().filter(|x| *x % 2 == 0).copied().collect();
scope.collect(evens)

Trait Implementations§

Source§

impl Drop for ProtectScope

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 Protector for ProtectScope

Source§

unsafe fn protect(&mut self, sexp: SEXP) -> SEXP

Protect a SEXP from garbage collection. 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> 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