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 protectionsFields§
§n: Cell<i32>§armed: Cell<bool>§_nosend: PhantomData<Rc<()>>Implementations§
Source§impl ProtectScope
impl ProtectScope
Sourcepub unsafe fn protect<'a>(&'a self, x: SEXP) -> Root<'a>
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
xmust be a valid SEXP
Sourcepub unsafe fn protect_raw(&self, x: SEXP) -> SEXP
pub unsafe fn protect_raw(&self, x: SEXP) -> SEXP
Sourcepub unsafe fn protect_with_index<'a>(&'a self, x: SEXP) -> ReprotectSlot<'a>
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
xmust 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()
}Sourcepub unsafe fn protect3<'a>(
&'a self,
a: SEXP,
b: SEXP,
c: SEXP,
) -> (Root<'a>, Root<'a>, Root<'a>)
pub unsafe fn protect3<'a>( &'a self, a: SEXP, b: SEXP, c: SEXP, ) -> (Root<'a>, Root<'a>, Root<'a>)
Sourcepub unsafe fn disarm(&self)
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.
Sourcepub unsafe fn rearm(&self)
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.
Sourcepub unsafe fn alloc_vector<'a>(&'a self, ty: SEXPTYPE, n: R_xlen_t) -> Root<'a>
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()
}Sourcepub unsafe fn alloc_matrix<'a>(
&'a self,
ty: SEXPTYPE,
nrow: i32,
ncol: i32,
) -> Root<'a>
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.
Sourcepub unsafe fn alloc_list<'a>(&'a self, n: i32) -> Root<'a>
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.
Sourcepub unsafe fn alloc_strsxp<'a>(&'a self, n: usize) -> Root<'a>
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.
Sourcepub unsafe fn alloc_vecsxp<'a>(&'a self, n: usize) -> Root<'a>
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.
Sourcepub unsafe fn alloc_vector_unchecked<'a>(
&'a self,
ty: SEXPTYPE,
n: R_xlen_t,
) -> Root<'a>
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”).
Sourcepub unsafe fn alloc_vecsxp_unchecked<'a>(&'a self, n: usize) -> Root<'a>
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.
Sourcepub unsafe fn alloc_character_unchecked<'a>(&'a self, n: usize) -> Root<'a>
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.
Sourcepub unsafe fn alloc_integer<'a>(&'a self, n: usize) -> Root<'a>
pub unsafe fn alloc_integer<'a>(&'a self, n: usize) -> Root<'a>
Sourcepub unsafe fn alloc_real<'a>(&'a self, n: usize) -> Root<'a>
pub unsafe fn alloc_real<'a>(&'a self, n: usize) -> Root<'a>
Sourcepub unsafe fn alloc_logical<'a>(&'a self, n: usize) -> Root<'a>
pub unsafe fn alloc_logical<'a>(&'a self, n: usize) -> Root<'a>
Sourcepub unsafe fn alloc_complex<'a>(&'a self, n: usize) -> Root<'a>
pub unsafe fn alloc_complex<'a>(&'a self, n: usize) -> Root<'a>
Sourcepub unsafe fn alloc_character<'a>(&'a self, n: usize) -> Root<'a>
pub unsafe fn alloc_character<'a>(&'a self, n: usize) -> Root<'a>
Sourcepub unsafe fn scalar_integer<'a>(&'a self, x: i32) -> Root<'a>
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.
Sourcepub unsafe fn scalar_real<'a>(&'a self, x: f64) -> Root<'a>
pub unsafe fn scalar_real<'a>(&'a self, x: f64) -> Root<'a>
Sourcepub unsafe fn scalar_logical<'a>(&'a self, x: bool) -> Root<'a>
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.
Sourcepub unsafe fn scalar_complex<'a>(&'a self, x: Rcomplex) -> Root<'a>
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.
Sourcepub unsafe fn scalar_raw<'a>(&'a self, x: u8) -> Root<'a>
pub unsafe fn scalar_raw<'a>(&'a self, x: u8) -> Root<'a>
Sourcepub unsafe fn scalar_string<'a>(&'a self, s: &str) -> Root<'a>
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.
Sourcepub unsafe fn duplicate<'a>(&'a self, x: SEXP) -> Root<'a>
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.
Sourcepub unsafe fn shallow_duplicate<'a>(&'a self, x: SEXP) -> Root<'a>
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.
Sourcepub unsafe fn coerce<'a>(&'a self, x: SEXP, target: SEXPTYPE) -> Root<'a>
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.
Sourcepub unsafe fn alloc_array<'a>(&'a self, ty: SEXPTYPE, dims: &[i32]) -> Root<'a>
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.
Sourcepub unsafe fn alloc_3d_array<'a>(
&'a self,
ty: SEXPTYPE,
nrow: i32,
ncol: i32,
nface: i32,
) -> Root<'a>
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.
Sourcepub unsafe fn alloc_lang<'a>(&'a self, n: i32) -> Root<'a>
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.
Sourcepub unsafe fn alloc_s4_object<'a>(&'a self) -> Root<'a>
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.
Sourcepub unsafe fn mkchar_len_ce<'a>(&'a self, s: &[u8], enc: cetype_t) -> Root<'a>
pub unsafe fn mkchar_len_ce<'a>(&'a self, s: &[u8], enc: cetype_t) -> Root<'a>
Sourcepub unsafe fn mkchar_ce<'a>(&'a self, s: &str, enc: cetype_t) -> Root<'a>
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.
Sourcepub unsafe fn cons<'a>(&'a self, car: SEXP, cdr: SEXP) -> Root<'a>
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.
Sourcepub unsafe fn lengthgets<'a>(&'a self, x: SEXP, n: R_xlen_t) -> Root<'a>
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.
Sourcepub unsafe fn xlengthgets<'a>(&'a self, x: SEXP, n: R_xlen_t) -> Root<'a>
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.
Sourcepub unsafe fn make_external_ptr<'a>(
&'a self,
p: *mut c_void,
tag: SEXP,
prot: SEXP,
) -> Root<'a>
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.
Sourcepub unsafe fn rooted<'a>(&'a self, sexp: SEXP) -> Root<'a>
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
sexpis already protected and will remain protected for at least the lifetime of this scope - Must be called from the R main thread
Sourcepub unsafe fn collect<'a, T, I>(&'a self, iter: I) -> Root<'a>
pub unsafe fn collect<'a, T, I>(&'a self, iter: I) -> Root<'a>
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
§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
impl Drop for ProtectScope
Auto Trait Implementations§
impl !Freeze for ProtectScope
impl !RefUnwindSafe for ProtectScope
impl !Send for ProtectScope
impl !Sync for ProtectScope
impl Unpin for ProtectScope
impl UnsafeUnpin for ProtectScope
impl UnwindSafe for ProtectScope
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