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_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 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)