miniextendr_api/externalptr.rs
1#![allow(rustdoc::private_intra_doc_links)]
2//! `ExternalPtr<T>` — a Box-like owned pointer that wraps R's EXTPTRSXP.
3//!
4//! This provides ownership semantics similar to `Box<T>`, with the key difference
5//! that cleanup is deferred to R's garbage collector via finalizers.
6//!
7//! # Submodules
8//!
9//! | Module | Contents |
10//! |--------|----------|
11//! | [`altrep_helpers`] | ALTREP data1/data2 slot access helpers + `Sidecar` marker type |
12//!
13//! # Core Types
14//!
15//! - [`ExternalPtr<T>`] — owned pointer wrapping EXTPTRSXP
16//! - [`TypedExternal`] — trait for type-safe identification across packages
17//! - [`ExternalSlice<T>`] — helper for slice data in external pointers
18//! - [`ErasedExternalPtr`] — type-erased `ExternalPtr<()>` alias
19//! - [`IntoExternalPtr`] — conversion trait for wrapping values
20//!
21//! `PartialEq`/`PartialOrd` compare the pointee values (like `Box<T>`). Use
22//! `ptr_eq` when you care about pointer identity, and `as_ref()`/`as_mut()` for
23//! explicit by-value comparisons.
24//!
25//! # Protection Strategies in miniextendr
26//!
27//! miniextendr provides three complementary protection mechanisms for different scenarios:
28//!
29//! | Strategy | Module | Lifetime | Release Order | Use Case |
30//! |----------|--------|----------|---------------|----------|
31//! | **PROTECT stack** | [`gc_protect`](crate::gc_protect) | Within `.Call` | LIFO (stack) | Temporary allocations |
32//! | **Preserve list** | [`preserve`](crate::preserve) | Across `.Call`s | Any order | Long-lived R objects |
33//! | **R ownership** | [`ExternalPtr`](struct@crate::externalptr::ExternalPtr) | Until R GCs | R decides | Rust data owned by R |
34//!
35//! ## When to Use ExternalPtr
36//!
37//! **Use `ExternalPtr` (this module) when:**
38//! - You want R to own a Rust value
39//! - The Rust value should be dropped when R garbage collects the pointer
40//! - You're exposing Rust structs to R code
41//!
42//! **Use [`gc_protect`](crate::gc_protect) instead when:**
43//! - You're allocating temporary R objects during computation
44//! - Protection is short-lived (within a single `.Call`)
45//!
46//! **Use [`preserve`](crate::preserve) instead when:**
47//! - You need R objects (not Rust values) to survive across `.Call`s
48//! - You need arbitrary-order release of protections
49//!
50//! ## How ExternalPtr Protection Works
51//!
52//! ```text
53//! ┌─────────────────────────────────────────────────────────────────┐
54//! │ ExternalPtr<MyStruct>::new(value) │
55//! │ ├── Rf_protect() during construction (temporary) │
56//! │ ├── R_MakeExternalPtr() creates EXTPTRSXP │
57//! │ ├── R_RegisterCFinalizerEx() registers cleanup callback │
58//! │ └── Rf_unprotect() after construction complete │
59//! │ │
60//! │ Return to R → R now owns the EXTPTRSXP │
61//! │ ├── SEXP is live as long as R has references │
62//! │ └── Rust value is accessible via ExternalPtr::wrap_sexp() │
63//! │ │
64//! │ R GC runs → finalizer called → Rust Drop executes │
65//! └─────────────────────────────────────────────────────────────────┘
66//! ```
67//!
68//! # Type Identification
69//!
70//! Type safety is enforced via `Any::downcast` (Rust's `TypeId`). R symbols
71//! in the `tag` and `prot` slots are retained for display and error messages.
72//!
73//! Internally, data is stored as `Box<Box<dyn Any>>` — a thin pointer (fits
74//! in R's `R_ExternalPtrAddr`) pointing to a fat pointer (carries the `Any`
75//! vtable for runtime downcasting).
76//!
77//! The `tag` slot holds a symbol (type name, for display).
78//! The `prot` slot holds a VECSXP (list) with two elements:
79//! - Index 0: SYMSXP (interned type ID symbol, for error messages)
80//! - Index 1: User-protected SEXP slot (for preventing GC of R objects)
81//!
82//! # ExternalPtr is Not an R Native Type
83//!
84//! Unlike R's native atomic types (`integer`, `double`, `character`, etc.),
85//! external pointers cannot be coerced to vectors or used in R's vectorized
86//! operations. This is an R limitation, not a miniextendr limitation:
87//!
88//! ```r
89//! > matrix(new("externalptr"), 1, 1)
90//! Error in `as.vector()`:
91//! ! cannot coerce type 'externalptr' to vector of type 'any'
92//! ```
93//!
94//! If you need your Rust type to participate in R's vector/matrix operations,
95//! consider implementing [`IntoList`](crate::list::IntoList) (via `#[derive(IntoList)]`)
96//! to convert your struct to a named R list, or use ALTREP to expose Rust
97//! iterators as lazy R vectors.
98
99use std::any::Any;
100use std::any::TypeId;
101use std::fmt;
102use std::hash::{Hash, Hasher};
103use std::marker::PhantomData;
104use std::mem::{self, ManuallyDrop, MaybeUninit};
105use std::ops::{Deref, DerefMut};
106use std::pin::Pin;
107use std::ptr::{self, NonNull};
108
109use crate::ffi::{
110 R_ClearExternalPtr, R_ExternalPtrAddr, R_ExternalPtrProtected, R_ExternalPtrTag,
111 R_MakeExternalPtr, R_MakeExternalPtr_unchecked, R_RegisterCFinalizerEx,
112 R_RegisterCFinalizerEx_unchecked, Rboolean, Rf_allocVector, Rf_allocVector_unchecked,
113 Rf_install, Rf_install_unchecked, Rf_protect, Rf_protect_unchecked, Rf_unprotect,
114 Rf_unprotect_unchecked, SEXP, SEXPTYPE, SexpExt,
115};
116
117/// A wrapper around a raw pointer that implements [`Send`].
118///
119/// # Safety
120///
121/// This is safe to send between threads because it's just a memory address.
122/// The data is owned and transferred to the main thread before being accessed.
123type SendableAnyPtr = crate::worker::Sendable<NonNull<Box<dyn Any>>>;
124
125/// Create a new sendable pointer from a raw `*mut Box<dyn Any>`.
126///
127/// # Safety
128///
129/// The pointer must be non-null.
130#[inline]
131unsafe fn sendable_any_ptr_new(ptr: *mut Box<dyn Any>) -> SendableAnyPtr {
132 // SAFETY: Caller guarantees ptr is non-null
133 crate::worker::Sendable(unsafe { NonNull::new_unchecked(ptr) })
134}
135
136/// Get the raw pointer, consuming the sendable wrapper.
137#[inline]
138fn sendable_any_ptr_into_ptr(ptr: SendableAnyPtr) -> *mut Box<dyn Any> {
139 ptr.0.as_ptr()
140}
141
142/// Index of the type SYMSXP contained in the `prot` (a `VECSXP` list)
143const PROT_TYPE_ID_INDEX: isize = 0;
144/// Index of user-protected objects contained in the `prot` (a `VECSXP` list)
145const PROT_USER_INDEX: isize = 1;
146/// Length of the `prot` list (`VECSXP`)
147const PROT_VEC_LEN: isize = 2;
148
149#[inline]
150fn is_type_erased<T: 'static>() -> bool {
151 TypeId::of::<T>() == TypeId::of::<()>()
152}
153
154/// Get the interned R symbol for a type's name.
155///
156/// R interns symbols via `Rf_install`, so the same string always returns
157/// the same pointer. This enables fast pointer comparison for type checking.
158///
159/// # Safety
160///
161/// Must be called from R's main thread.
162#[inline]
163unsafe fn type_symbol<T: TypedExternal>() -> SEXP {
164 unsafe { Rf_install(T::TYPE_NAME_CSTR.as_ptr().cast()) }
165}
166
167/// Unchecked version of [`type_symbol`] - no thread safety checks.
168///
169/// # Safety
170///
171/// Must be called from R's main thread. No debug assertions.
172#[inline]
173unsafe fn type_symbol_unchecked<T: TypedExternal>() -> SEXP {
174 unsafe { Rf_install_unchecked(T::TYPE_NAME_CSTR.as_ptr().cast()) }
175}
176
177/// Get the namespaced type ID symbol for type checking.
178///
179/// Uses `TYPE_ID_CSTR` which includes the module path for uniqueness.
180///
181/// # Safety
182///
183/// Must be called from R's main thread.
184#[inline]
185unsafe fn type_id_symbol<T: TypedExternal>() -> SEXP {
186 unsafe { Rf_install(T::TYPE_ID_CSTR.as_ptr().cast()) }
187}
188
189/// Unchecked version of [`type_id_symbol`].
190///
191/// # Safety
192///
193/// Must be called from R's main thread. No debug assertions.
194#[inline]
195unsafe fn type_id_symbol_unchecked<T: TypedExternal>() -> SEXP {
196 unsafe { Rf_install_unchecked(T::TYPE_ID_CSTR.as_ptr().cast()) }
197}
198
199/// Get the type name from a stored symbol SEXP.
200///
201/// # Safety
202///
203/// `sym` must be a valid SYMSXP.
204#[inline]
205fn symbol_name(sym: SEXP) -> &'static str {
206 use crate::ffi::SexpExt;
207 let printname = sym.printname();
208 let cstr = printname.r_char();
209 let len = printname.len();
210 unsafe {
211 std::str::from_utf8(std::slice::from_raw_parts(cstr.cast(), len))
212 .expect("R SYMSXP PRINTNAME is not valid UTF-8")
213 }
214}
215
216// region: TypedExternalPtr Trait
217
218/// Trait for types that can be stored in an ExternalPtr.
219///
220/// This provides the type identification needed for runtime type checking.
221/// Type identification uses R's symbol interning (`Rf_install`) for fast
222/// pointer-based comparison.
223///
224/// # Type ID vs Type Name
225///
226/// - `TYPE_ID_CSTR`: Namespaced identifier used for type checking (stored in `prot[0]`).
227/// Format: `"<crate_name>@<crate_version>::<module_path>::<type_name>\0"`
228///
229/// The crate name and version ensure:
230/// - Same type from same crate+version → compatible (can share ExternalPtr)
231/// - Same type name from different crates → incompatible
232/// - Same type from different crate versions → incompatible
233///
234/// - `TYPE_NAME_CSTR`: Short display name for the R tag (shown when printing).
235/// Just the type identifier for readability.
236pub trait TypedExternal: 'static {
237 /// The type name as a static string (for debugging and display)
238 const TYPE_NAME: &'static str;
239
240 /// The type name as a null-terminated C string (for R tag display)
241 const TYPE_NAME_CSTR: &'static [u8];
242
243 /// Namespaced type ID as a null-terminated C string (for type checking).
244 ///
245 /// This should include the module path to prevent cross-package collisions.
246 /// Use `concat!(module_path!(), "::", stringify!(Type), "\0").as_bytes()`
247 /// when implementing manually, or use `#[derive(ExternalPtr)]`.
248 const TYPE_ID_CSTR: &'static [u8];
249}
250
251/// Marker trait for types that should be converted to R as ExternalPtr.
252///
253/// When a type implements this trait (via `#[derive(ExternalPtr)]`), it gets a
254/// blanket `IntoR` implementation that wraps the value in `ExternalPtr<T>`.
255///
256/// This allows returning the type directly from `#[miniextendr]` functions:
257///
258/// ```ignore
259/// #[derive(ExternalPtr)]
260/// struct MyData { value: i32 }
261///
262/// #[miniextendr]
263/// fn create_data(v: i32) -> MyData {
264/// MyData { value: v } // Automatically wrapped in ExternalPtr
265/// }
266/// ```
267pub trait IntoExternalPtr: TypedExternal {}
268
269impl TypedExternal for () {
270 const TYPE_NAME: &'static str = "()";
271 const TYPE_NAME_CSTR: &'static [u8] = b"()\0";
272 // Unit type is special - same ID as name since it's only used for type-erased ptrs
273 const TYPE_ID_CSTR: &'static [u8] = b"()\0";
274}
275// endregion
276
277// region: ExternalPtr<T>
278
279/// An owned pointer stored in R's external pointer SEXP.
280///
281/// This is conceptually similar to `Box<T>`, but with the following differences:
282/// - Memory is freed by R's GC via a registered finalizer (non-deterministic)
283/// - The underlying SEXP is Copy, so aliasing must be manually prevented
284/// - Type checking happens at runtime via `Any::downcast` (Rust `TypeId`)
285///
286/// # Thread Safety
287///
288/// `ExternalPtr` is `Send` to allow returning from worker thread functions.
289/// However, **concurrent access is not allowed** - R's runtime is single-threaded.
290/// All R API calls are serialized through the main thread via `with_r_thread`.
291///
292/// # Safety
293///
294/// The ExternalPtr assumes exclusive ownership of the underlying data.
295/// Cloning the raw SEXP without proper handling will lead to double-free.
296///
297/// # Examples
298///
299/// ```no_run
300/// use miniextendr_api::externalptr::{ExternalPtr, TypedExternal};
301///
302/// struct MyData { value: f64 }
303/// impl TypedExternal for MyData {
304/// const TYPE_NAME: &'static str = "MyData";
305/// const TYPE_NAME_CSTR: &'static [u8] = b"MyData\0";
306/// const TYPE_ID_CSTR: &'static [u8] = b"my_crate::MyData\0";
307/// }
308///
309/// let ptr = ExternalPtr::new(MyData { value: 3.14 });
310/// assert_eq!(ptr.as_ref().unwrap().value, 3.14);
311/// ```
312#[repr(C)]
313pub struct ExternalPtr<T: TypedExternal> {
314 sexp: SEXP,
315 /// Cached data pointer, set once at construction time.
316 ///
317 /// This avoids the `R_ExternalPtrAddr` FFI call on every `as_ref()`/`as_mut()`.
318 /// The pointer remains valid for the lifetime of the `ExternalPtr` because:
319 /// - R's finalizer only runs after R garbage-collects the SEXP (which cannot
320 /// happen while a Rust `ExternalPtr` value exists).
321 /// - `R_ClearExternalPtr` is only called in methods that consume or finalize
322 /// (`into_raw`, `into_inner`, `release_any`).
323 cached_ptr: NonNull<T>,
324 _marker: PhantomData<T>,
325}
326
327// SAFETY: ExternalPtr can be sent between threads because:
328// 1. All R API operations are serialized through the main thread via with_r_thread
329// 2. The worker thread is blocked while the main thread processes R calls
330// 3. There is no concurrent access - only sequential hand-off between threads
331unsafe impl<T: TypedExternal + Send> Send for ExternalPtr<T> {}
332
333impl<T: TypedExternal> ExternalPtr<T> {
334 /// Allocates memory on the heap and places `x` into it.
335 ///
336 /// Internally stores a `Box<Box<dyn Any>>` — a thin pointer (fits in R's
337 /// `R_ExternalPtrAddr`) pointing to a fat pointer (carries the `Any` vtable
338 /// for runtime type checking via `downcast`).
339 ///
340 /// This function can be called from any thread:
341 /// - If called from R's main thread, creates the ExternalPtr directly
342 /// - If called from the worker thread (during `run_on_worker`), automatically
343 /// sends the R API calls to the main thread via [`with_r_thread`]
344 ///
345 /// # Panics
346 ///
347 /// Panics if called from a non-main thread outside of a `run_on_worker` context.
348 ///
349 /// Equivalent to `Box::new`.
350 ///
351 /// [`with_r_thread`]: crate::worker::with_r_thread
352 #[inline]
353 pub fn new(x: T) -> Self {
354 // Get concrete pointer with full write provenance from Box::into_raw,
355 // BEFORE erasing to dyn Any. This preserves mutable provenance for
356 // cached_ptr (downcast_ref would give shared-reference provenance,
357 // which is UB for later writes through as_mut()).
358 let raw: *mut T = Box::into_raw(Box::new(x));
359 // SAFETY: Box::into_raw never returns null
360 let cached_ptr = unsafe { NonNull::new_unchecked(raw) };
361
362 // Re-wrap: Box::from_raw(raw) → Box<dyn Any> → Box<Box<dyn Any>>
363 // The data stays at `raw`; we're just adding the Any vtable wrapper.
364 let inner: Box<dyn Any> = unsafe { Box::from_raw(raw) };
365 let any_raw: *mut Box<dyn Any> = Box::into_raw(Box::new(inner));
366
367 // Wrap in Sendable so it can be sent across thread boundary
368 let sendable = unsafe { sendable_any_ptr_new(any_raw) };
369
370 // Use with_r_thread to run R API calls on main thread
371 let sexp = crate::worker::with_r_thread(move || {
372 let any_raw = sendable_any_ptr_into_ptr(sendable);
373 unsafe { Self::create_extptr_sexp_unchecked(any_raw) }
374 });
375
376 Self {
377 sexp,
378 cached_ptr,
379 _marker: PhantomData,
380 }
381 }
382
383 /// Allocates memory on the heap and places `x` into it, without thread checks.
384 ///
385 /// # Safety
386 ///
387 /// Must be called from R's main thread. Calling from another thread
388 /// is undefined behavior (R APIs are not thread-safe).
389 #[inline]
390 pub unsafe fn new_unchecked(x: T) -> Self {
391 let raw: *mut T = Box::into_raw(Box::new(x));
392 let cached_ptr = unsafe { NonNull::new_unchecked(raw) };
393
394 let inner: Box<dyn Any> = unsafe { Box::from_raw(raw) };
395 let any_raw: *mut Box<dyn Any> = Box::into_raw(Box::new(inner));
396
397 let sexp = unsafe { Self::create_extptr_sexp_unchecked(any_raw) };
398 Self {
399 sexp,
400 cached_ptr,
401 _marker: PhantomData,
402 }
403 }
404
405 /// Create an EXTPTRSXP from a `*mut Box<dyn Any>`. Must be called from main thread.
406 ///
407 /// The `any_raw` is a thin pointer to a heap-allocated fat pointer (`Box<dyn Any>`).
408 /// R stores the thin pointer in `R_ExternalPtrAddr`.
409 #[inline]
410 unsafe fn create_extptr_sexp(any_raw: *mut Box<dyn Any>) -> SEXP {
411 debug_assert!(
412 !any_raw.is_null(),
413 "create_extptr_sexp received null pointer"
414 );
415
416 let type_sym = unsafe { type_symbol::<T>() };
417 let type_id_sym = unsafe { type_id_symbol::<T>() };
418
419 let prot = unsafe { Rf_allocVector(SEXPTYPE::VECSXP, PROT_VEC_LEN) };
420 unsafe { Rf_protect(prot) };
421 prot.set_vector_elt(PROT_TYPE_ID_INDEX, type_id_sym);
422
423 let sexp = unsafe { R_MakeExternalPtr(any_raw.cast(), type_sym, prot) };
424 unsafe { Rf_protect(sexp) };
425
426 // Non-generic finalizer — Box<dyn Any> vtable handles the concrete drop
427 unsafe { R_RegisterCFinalizerEx(sexp, Some(release_any), Rboolean::TRUE) };
428
429 unsafe { Rf_unprotect(2) };
430 sexp
431 }
432
433 /// Create an EXTPTRSXP from a `*mut Box<dyn Any>` without thread safety checks.
434 ///
435 /// # Safety
436 ///
437 /// Must be called from R's main thread. No debug assertions for thread safety.
438 #[inline]
439 unsafe fn create_extptr_sexp_unchecked(any_raw: *mut Box<dyn Any>) -> SEXP {
440 debug_assert!(
441 !any_raw.is_null(),
442 "create_extptr_sexp_unchecked received null pointer"
443 );
444
445 let type_sym = unsafe { type_symbol_unchecked::<T>() };
446 let type_id_sym = unsafe { type_id_symbol_unchecked::<T>() };
447
448 let prot = unsafe { Rf_allocVector_unchecked(SEXPTYPE::VECSXP, PROT_VEC_LEN) };
449 unsafe { Rf_protect_unchecked(prot) };
450 unsafe { prot.set_vector_elt_unchecked(PROT_TYPE_ID_INDEX, type_id_sym) };
451
452 let sexp = unsafe { R_MakeExternalPtr_unchecked(any_raw.cast(), type_sym, prot) };
453 unsafe { Rf_protect_unchecked(sexp) };
454
455 // Non-generic finalizer — Box<dyn Any> vtable handles the concrete drop
456 unsafe {
457 R_RegisterCFinalizerEx_unchecked(sexp, Some(release_any), Rboolean::TRUE);
458 };
459
460 unsafe { Rf_unprotect_unchecked(2) };
461 sexp
462 }
463
464 /// Constructs a new `ExternalPtr` with uninitialized contents.
465 ///
466 /// Equivalent to `Box::new_uninit`.
467 #[inline]
468 pub fn new_uninit() -> ExternalPtr<MaybeUninit<T>>
469 where
470 MaybeUninit<T>: TypedExternal,
471 {
472 ExternalPtr::new(MaybeUninit::uninit())
473 }
474
475 /// Constructs a new `ExternalPtr` with zeroed contents.
476 ///
477 /// Equivalent to `Box::new_zeroed`.
478 #[inline]
479 pub fn new_zeroed() -> ExternalPtr<MaybeUninit<T>>
480 where
481 MaybeUninit<T>: TypedExternal,
482 {
483 ExternalPtr::new(MaybeUninit::zeroed())
484 }
485
486 /// Constructs an ExternalPtr from a raw pointer.
487 ///
488 /// Re-wraps the `*mut T` in `Box<dyn Any>` for the new storage format.
489 ///
490 /// # Safety
491 ///
492 /// - `raw` must have been allocated via `Box::into_raw` or equivalent
493 /// - `raw` must not be null
494 /// - Caller transfers ownership to the ExternalPtr
495 /// - Must be called from R's main thread
496 ///
497 /// Equivalent to `Box::from_raw`.
498 #[inline]
499 pub unsafe fn from_raw(raw: *mut T) -> Self {
500 // Re-wrap in Box<dyn Any> → Box<Box<dyn Any>>
501 let inner: Box<dyn Any> = unsafe { Box::from_raw(raw) };
502 let outer: Box<Box<dyn Any>> = Box::new(inner);
503 let any_raw: *mut Box<dyn Any> = Box::into_raw(outer);
504
505 let sexp = unsafe { Self::create_extptr_sexp(any_raw) };
506 Self {
507 sexp,
508 cached_ptr: unsafe { NonNull::new_unchecked(raw) },
509 _marker: PhantomData,
510 }
511 }
512
513 /// Constructs an ExternalPtr from a raw pointer, without thread checks.
514 ///
515 /// # Safety
516 ///
517 /// - `raw` must have been allocated via `Box::into_raw` or equivalent
518 /// - `raw` must not be null
519 /// - Caller transfers ownership to the ExternalPtr
520 /// - Must be called from R's main thread (no debug assertions)
521 #[inline]
522 pub unsafe fn from_raw_unchecked(raw: *mut T) -> Self {
523 let inner: Box<dyn Any> = unsafe { Box::from_raw(raw) };
524 let outer: Box<Box<dyn Any>> = Box::new(inner);
525 let any_raw: *mut Box<dyn Any> = Box::into_raw(outer);
526
527 let sexp = unsafe { Self::create_extptr_sexp_unchecked(any_raw) };
528 Self {
529 sexp,
530 cached_ptr: unsafe { NonNull::new_unchecked(raw) },
531 _marker: PhantomData,
532 }
533 }
534
535 /// Consumes the ExternalPtr, returning a raw pointer.
536 ///
537 /// The caller is responsible for the memory, and the finalizer is
538 /// effectively orphaned (will do nothing since we clear the pointer).
539 ///
540 /// Equivalent to `Box::into_raw`.
541 #[inline]
542 pub fn into_raw(this: Self) -> *mut T {
543 let ptr = this.cached_ptr.as_ptr();
544
545 // Recover and disassemble the Box<Box<dyn Any>> wrapper.
546 // We need to free the wrapper allocations without dropping the T data.
547 let any_raw = unsafe { R_ExternalPtrAddr(this.sexp) as *mut Box<dyn Any> };
548
549 // Clear the external pointer so the finalizer becomes a no-op
550 unsafe { R_ClearExternalPtr(this.sexp) };
551
552 if !any_raw.is_null() {
553 // Reconstruct outer box → extract inner → leak inner (prevents T drop)
554 let outer: Box<Box<dyn Any>> = unsafe { Box::from_raw(any_raw) };
555 let inner: Box<dyn Any> = *outer;
556 // Box::into_raw leaks the inner allocation — caller owns T via `ptr`
557 let _ = Box::into_raw(inner);
558 }
559
560 // Don't run our Drop
561 mem::forget(this);
562
563 ptr
564 }
565
566 /// Consumes the ExternalPtr, returning a `NonNull` pointer.
567 ///
568 /// Equivalent to `Box::into_non_null`.
569 #[inline]
570 pub fn into_non_null(this: Self) -> NonNull<T> {
571 unsafe { NonNull::new_unchecked(Self::into_raw(this)) }
572 }
573
574 /// Consumes and leaks the ExternalPtr, returning a mutable reference.
575 ///
576 /// The memory will never be freed (from Rust's perspective; R's GC
577 /// finalizer is neutralized).
578 ///
579 /// Equivalent to `Box::leak`.
580 #[inline]
581 pub fn leak<'a>(this: Self) -> &'a mut T
582 where
583 T: 'a,
584 {
585 unsafe { &mut *Self::into_raw(this) }
586 }
587
588 /// Consumes the ExternalPtr, returning the wrapped value.
589 ///
590 /// Uses `Box<dyn Any>::downcast` to recover the concrete `Box<T>`,
591 /// then moves the value out.
592 ///
593 /// Equivalent to `*boxed` (deref move) or `Box::into_inner`.
594 #[inline]
595 pub fn into_inner(this: Self) -> T {
596 let any_raw = unsafe { R_ExternalPtrAddr(this.sexp) as *mut Box<dyn Any> };
597
598 // Clear so finalizer is no-op
599 unsafe { R_ClearExternalPtr(this.sexp) };
600 mem::forget(this);
601
602 assert!(!any_raw.is_null(), "ExternalPtr is null or cleared");
603 let outer: Box<Box<dyn Any>> = unsafe { Box::from_raw(any_raw) };
604 let inner: Box<dyn Any> = *outer;
605 *inner
606 .downcast::<T>()
607 .expect("ExternalPtr type mismatch in into_inner")
608 }
609
610 // region: Pin support (Box-equivalent)
611
612 /// Constructs a new `Pin<ExternalPtr<T>>`.
613 ///
614 /// Equivalent to `Box::pin`.
615 ///
616 /// # Note
617 ///
618 /// Unlike `Box::pin`, this requires `T: Unpin` because `ExternalPtr`
619 /// implements `DerefMut` unconditionally. For `!Unpin` types, use
620 /// `ExternalPtr::new` and manage pinning guarantees manually.
621 #[inline]
622 pub fn pin(x: T) -> Pin<Self>
623 where
624 T: Unpin,
625 {
626 // SAFETY: T: Unpin, so pinning is always safe
627 Pin::new(Self::new(x))
628 }
629
630 /// Constructs a new `Pin<ExternalPtr<T>>` without requiring `Unpin`.
631 ///
632 /// # Safety
633 ///
634 /// The caller must ensure that the pinning invariants are upheld:
635 /// - The data will not be moved out of the `ExternalPtr`
636 /// - The data will not be accessed mutably in ways that would move it
637 ///
638 /// Since `ExternalPtr` implements `DerefMut`, using this with `!Unpin`
639 /// types requires careful handling to avoid moving the inner value.
640 #[inline]
641 pub fn pin_unchecked(x: T) -> Pin<Self> {
642 unsafe { Pin::new_unchecked(Self::new(x)) }
643 }
644
645 /// Converts a `ExternalPtr<T>` into a `Pin<ExternalPtr<T>>`.
646 ///
647 /// Equivalent to `Box::into_pin`.
648 #[inline]
649 pub fn into_pin(this: Self) -> Pin<Self>
650 where
651 T: Unpin,
652 {
653 // SAFETY: T: Unpin, so it's always safe to pin
654 Pin::new(this)
655 }
656 // endregion
657
658 // region: Accessors
659
660 /// Returns a reference to the underlying value.
661 ///
662 /// Uses the cached pointer set at construction time, avoiding the
663 /// `R_ExternalPtrAddr` FFI call on every access.
664 #[inline]
665 pub fn as_ref(&self) -> Option<&T> {
666 // SAFETY: cached_ptr is always valid for the lifetime of ExternalPtr
667 Some(unsafe { self.cached_ptr.as_ref() })
668 }
669
670 /// Returns a mutable reference to the underlying value.
671 ///
672 /// Uses the cached pointer set at construction time, avoiding the
673 /// `R_ExternalPtrAddr` FFI call on every access.
674 #[inline]
675 pub fn as_mut(&mut self) -> Option<&mut T> {
676 // SAFETY: cached_ptr is always valid for the lifetime of ExternalPtr
677 Some(unsafe { self.cached_ptr.as_mut() })
678 }
679
680 /// Returns the raw pointer without consuming the ExternalPtr.
681 #[inline]
682 pub fn as_ptr(&self) -> *const T {
683 self.cached_ptr.as_ptr().cast_const()
684 }
685
686 /// Returns the raw mutable pointer without consuming the ExternalPtr.
687 #[inline]
688 pub fn as_mut_ptr(&mut self) -> *mut T {
689 self.cached_ptr.as_ptr()
690 }
691
692 /// Checks whether two `ExternalPtr`s refer to the same allocation (pointer identity).
693 ///
694 /// This ignores the pointee values. Use this when you need alias detection;
695 /// prefer `PartialEq`/`PartialOrd` or `as_ref()` for value comparisons.
696 #[inline]
697 pub fn ptr_eq(this: &Self, other: &Self) -> bool {
698 ptr::eq(
699 this.cached_ptr.as_ptr().cast_const(),
700 other.cached_ptr.as_ptr().cast_const(),
701 )
702 }
703 // endregion
704
705 // region: R-specific accessors
706
707 /// Returns the underlying SEXP.
708 ///
709 /// # Warning
710 ///
711 /// The returned SEXP must not be duplicated or the finalizer will double-free.
712 #[inline]
713 pub fn as_sexp(&self) -> SEXP {
714 self.sexp
715 }
716
717 /// Create a lightweight alias of this ExternalPtr sharing the same R object.
718 ///
719 /// The returned `ExternalPtr` points to the **same** underlying EXTPTRSXP.
720 /// No data is copied and no new R object is allocated -- both the original
721 /// and the alias refer to the same R-level external pointer.
722 ///
723 /// This is the correct way to return "self" from a method that takes
724 /// `self: &ExternalPtr<Self>`, preserving R object identity:
725 ///
726 /// ```ignore
727 /// #[miniextendr(env)]
728 /// impl MyType {
729 /// pub fn identity(self: &ExternalPtr<Self>) -> ExternalPtr<Self> {
730 /// self.reborrow()
731 /// }
732 /// }
733 /// ```
734 ///
735 /// # Safety note
736 ///
737 /// The caller must not use the original and the alias to create overlapping
738 /// mutable references (`as_mut`). In typical use (returning from a method),
739 /// the borrow of the original ends when the method returns, so this is safe.
740 #[inline]
741 pub fn reborrow(&self) -> Self {
742 // SAFETY: self.sexp is a valid live EXTPTRSXP that we already hold.
743 // wrap_sexp re-extracts the data pointer from the same SEXP.
744 unsafe { Self::wrap_sexp(self.sexp) }
745 .expect("reborrow of live ExternalPtr should never fail")
746 }
747
748 /// Returns the tag SEXP (type identifier symbol).
749 #[inline]
750 pub fn tag(&self) -> SEXP {
751 unsafe { R_ExternalPtrTag(self.sexp) }
752 }
753
754 /// Returns the tag SEXP (unchecked version).
755 ///
756 /// Skips thread safety checks for performance-critical paths.
757 ///
758 /// # Safety
759 ///
760 /// Must be called from the R main thread. Only use in ALTREP callbacks
761 /// or other contexts where you're certain you're on the main thread.
762 #[inline]
763 pub unsafe fn tag_unchecked(&self) -> SEXP {
764 unsafe { crate::ffi::R_ExternalPtrTag_unchecked(self.sexp) }
765 }
766
767 /// Returns the protected SEXP slot (user-protected objects).
768 ///
769 /// This returns the user-protected object stored in the prot VECSXP,
770 /// not the VECSXP itself.
771 #[inline]
772 pub fn protected(&self) -> SEXP {
773 unsafe {
774 let prot = R_ExternalPtrProtected(self.sexp);
775 if prot.is_null_or_nil() {
776 return SEXP::nil();
777 }
778 if prot.type_of() != SEXPTYPE::VECSXP || prot.len() < PROT_VEC_LEN as usize {
779 return SEXP::nil();
780 }
781 prot.vector_elt(PROT_USER_INDEX)
782 }
783 }
784
785 /// Returns the protected SEXP slot (unchecked version).
786 ///
787 /// Skips thread safety checks for performance-critical paths.
788 ///
789 /// # Safety
790 ///
791 /// Must be called from the R main thread. Only use in ALTREP callbacks
792 /// or other contexts where you're certain you're on the main thread.
793 #[inline]
794 pub unsafe fn protected_unchecked(&self) -> SEXP {
795 use crate::ffi::R_ExternalPtrProtected_unchecked;
796
797 unsafe {
798 let prot = R_ExternalPtrProtected_unchecked(self.sexp);
799 if prot.is_null_or_nil() {
800 return SEXP::nil();
801 }
802 if prot.type_of() != SEXPTYPE::VECSXP || prot.len() < PROT_VEC_LEN as usize {
803 return SEXP::nil();
804 }
805 prot.vector_elt_unchecked(PROT_USER_INDEX)
806 }
807 }
808
809 /// Sets the user-protected SEXP slot.
810 ///
811 /// Use this to prevent R objects from being GC'd while this ExternalPtr exists.
812 /// The type ID stored in prot slot 0 is preserved.
813 ///
814 /// Returns `false` if the prot structure is malformed (should not happen
815 /// for ExternalPtrs created by this library).
816 ///
817 /// # Safety
818 ///
819 /// - `user_prot` must be a valid SEXP or R_NilValue
820 /// - Must be called from the R main thread
821 #[inline]
822 pub unsafe fn set_protected(&self, user_prot: SEXP) -> bool {
823 unsafe {
824 let prot = R_ExternalPtrProtected(self.sexp);
825 if prot.is_null_or_nil() {
826 debug_assert!(false, "ExternalPtr prot slot is null or R_NilValue");
827 return false;
828 }
829 if prot.type_of() != SEXPTYPE::VECSXP || prot.len() < PROT_VEC_LEN as usize {
830 debug_assert!(
831 false,
832 "ExternalPtr prot slot is not a VECSXP of expected length"
833 );
834 return false;
835 }
836 prot.set_vector_elt(PROT_USER_INDEX, user_prot);
837 true
838 }
839 }
840
841 /// Returns the raw prot VECSXP (contains both type ID and user protected).
842 ///
843 /// Prefer using `protected()` for user data and `stored_type_id()` for type info.
844 #[inline]
845 pub fn prot_raw(&self) -> SEXP {
846 unsafe { R_ExternalPtrProtected(self.sexp) }
847 }
848
849 /// Checks if the internal pointer is null (already finalized or cleared).
850 #[inline]
851 pub fn is_null(&self) -> bool {
852 unsafe { R_ExternalPtrAddr(self.sexp).is_null() }
853 }
854 // endregion
855
856 // region: Type checking
857
858 /// Attempt to wrap a SEXP as an ExternalPtr with type checking.
859 ///
860 /// Uses `Any::downcast_ref` for authoritative type checking (Rust `TypeId`).
861 /// Falls back to R symbol comparison for type-erased `ExternalPtr<()>`.
862 ///
863 /// Returns `None` if:
864 /// - The internal pointer is null
865 /// - The stored `Box<dyn Any>` does not contain a `T`
866 ///
867 /// # Safety
868 ///
869 /// - `sexp` must be a valid EXTPTRSXP created by this library
870 /// - The caller must ensure no other ExternalPtr owns this SEXP
871 pub unsafe fn wrap_sexp(sexp: SEXP) -> Option<Self> {
872 debug_assert_eq!(
873 sexp.type_of(),
874 crate::ffi::SEXPTYPE::EXTPTRSXP,
875 "wrap_sexp: expected EXTPTRSXP, got {:?}",
876 sexp.type_of()
877 );
878 let any_raw = unsafe { R_ExternalPtrAddr(sexp) as *mut Box<dyn Any> };
879 if any_raw.is_null() {
880 return None;
881 }
882
883 if is_type_erased::<T>() {
884 // Type-erased path: skip downcast, just use the raw pointer
885 // (ExternalPtr<()> doesn't care about the concrete type)
886 return Some(Self {
887 sexp,
888 cached_ptr: unsafe { NonNull::new_unchecked(any_raw.cast::<T>()) },
889 _marker: PhantomData,
890 });
891 }
892
893 // Use downcast_mut (not downcast_ref) so cached_ptr gets mutable
894 // provenance — shared-reference provenance from downcast_ref would
895 // make later writes through as_mut() UB under Stacked Borrows.
896 let any_box: &mut Box<dyn Any> = unsafe { &mut *any_raw };
897 let concrete: &mut T = any_box.downcast_mut::<T>()?;
898
899 Some(Self {
900 sexp,
901 cached_ptr: unsafe { NonNull::new_unchecked(ptr::from_mut(concrete)) },
902 _marker: PhantomData,
903 })
904 }
905
906 /// Attempt to wrap a SEXP as an ExternalPtr (unchecked version).
907 ///
908 /// Skips thread safety checks for performance-critical paths like ALTREP callbacks.
909 ///
910 /// # Safety
911 ///
912 /// - `sexp` must be a valid EXTPTRSXP created by this library
913 /// - The caller must ensure exclusive ownership
914 /// - Must be called from the R main thread (guaranteed in ALTREP callbacks)
915 pub unsafe fn wrap_sexp_unchecked(sexp: SEXP) -> Option<Self> {
916 use crate::ffi::R_ExternalPtrAddr_unchecked;
917
918 debug_assert_eq!(
919 sexp.type_of(),
920 crate::ffi::SEXPTYPE::EXTPTRSXP,
921 "wrap_sexp_unchecked: expected EXTPTRSXP, got {:?}",
922 sexp.type_of()
923 );
924 let any_raw = unsafe { R_ExternalPtrAddr_unchecked(sexp) as *mut Box<dyn Any> };
925 if any_raw.is_null() {
926 return None;
927 }
928
929 if is_type_erased::<T>() {
930 return Some(Self {
931 sexp,
932 cached_ptr: unsafe { NonNull::new_unchecked(any_raw.cast::<T>()) },
933 _marker: PhantomData,
934 });
935 }
936
937 let any_box: &mut Box<dyn Any> = unsafe { &mut *any_raw };
938 let concrete: &mut T = any_box.downcast_mut::<T>()?;
939
940 Some(Self {
941 sexp,
942 cached_ptr: unsafe { NonNull::new_unchecked(ptr::from_mut(concrete)) },
943 _marker: PhantomData,
944 })
945 }
946
947 /// Attempt to wrap a SEXP as an ExternalPtr, returning an error with type info on mismatch.
948 ///
949 /// This is used by the [`TryFromSexp`] trait implementation.
950 ///
951 /// # Safety
952 ///
953 /// Same as [`wrap_sexp`](Self::wrap_sexp).
954 ///
955 /// [`TryFromSexp`]: crate::TryFromSexp
956 pub unsafe fn wrap_sexp_with_error(sexp: SEXP) -> Result<Self, TypeMismatchError> {
957 debug_assert_eq!(
958 sexp.type_of(),
959 crate::ffi::SEXPTYPE::EXTPTRSXP,
960 "wrap_sexp_with_error: expected EXTPTRSXP, got {:?}",
961 sexp.type_of()
962 );
963 let any_raw = unsafe { R_ExternalPtrAddr(sexp) as *mut Box<dyn Any> };
964 if any_raw.is_null() {
965 return Err(TypeMismatchError::NullPointer);
966 }
967
968 if is_type_erased::<T>() {
969 return Ok(Self {
970 sexp,
971 cached_ptr: unsafe { NonNull::new_unchecked(any_raw.cast::<T>()) },
972 _marker: PhantomData,
973 });
974 }
975
976 let any_box: &mut Box<dyn Any> = unsafe { &mut *any_raw };
977 match any_box.downcast_mut::<T>() {
978 Some(concrete) => Ok(Self {
979 sexp,
980 cached_ptr: unsafe { NonNull::new_unchecked(ptr::from_mut(concrete)) },
981 _marker: PhantomData,
982 }),
983 None => {
984 // Try to get the stored type name from R symbol for error reporting
985 let found = unsafe {
986 let prot = R_ExternalPtrProtected(sexp);
987 if !prot.is_null_or_nil()
988 && prot.type_of() == SEXPTYPE::VECSXP
989 && prot.len() >= PROT_VEC_LEN as usize
990 {
991 let stored_sym = prot.vector_elt(PROT_TYPE_ID_INDEX);
992 if stored_sym.type_of() == SEXPTYPE::SYMSXP {
993 symbol_name(stored_sym)
994 } else {
995 "<unknown>"
996 }
997 } else {
998 "<unknown>"
999 }
1000 };
1001 Err(TypeMismatchError::Mismatch {
1002 expected: T::TYPE_NAME,
1003 found,
1004 })
1005 }
1006 }
1007 }
1008
1009 /// Create an ExternalPtr from an SEXP without type checking.
1010 ///
1011 /// # Safety
1012 ///
1013 /// - `sexp` must be a valid EXTPTRSXP containing a `*mut Box<dyn Any>`
1014 /// wrapping a value of type `T`
1015 /// - The caller must ensure exclusive ownership
1016 #[inline]
1017 pub unsafe fn from_sexp_unchecked(sexp: SEXP) -> Self {
1018 debug_assert_eq!(
1019 sexp.type_of(),
1020 crate::ffi::SEXPTYPE::EXTPTRSXP,
1021 "from_sexp_unchecked: expected EXTPTRSXP, got {:?}",
1022 sexp.type_of()
1023 );
1024 let any_raw = unsafe { R_ExternalPtrAddr(sexp) as *mut Box<dyn Any> };
1025 debug_assert!(!any_raw.is_null(), "from_sexp_unchecked: null pointer");
1026
1027 let cached_ptr = if is_type_erased::<T>() {
1028 unsafe { NonNull::new_unchecked(any_raw.cast::<T>()) }
1029 } else {
1030 let any_box: &mut Box<dyn Any> = unsafe { &mut *any_raw };
1031 let concrete: &mut T = unsafe { any_box.downcast_mut::<T>().unwrap_unchecked() };
1032 unsafe { NonNull::new_unchecked(ptr::from_mut(concrete)) }
1033 };
1034
1035 Self {
1036 sexp,
1037 cached_ptr,
1038 _marker: PhantomData,
1039 }
1040 }
1041 // endregion
1042
1043 // region: Downcast support
1044
1045 /// Returns the type name for type T.
1046 #[inline]
1047 pub fn type_name() -> &'static str {
1048 T::TYPE_NAME
1049 }
1050
1051 /// Returns the type name stored in this ExternalPtr's prot slot.
1052 ///
1053 /// Returns `None` if the prot slot doesn't contain a valid type symbol.
1054 #[inline]
1055 pub fn stored_type_name(&self) -> Option<&'static str> {
1056 unsafe {
1057 let prot = R_ExternalPtrProtected(self.sexp);
1058 if prot.is_null_or_nil() {
1059 return None;
1060 }
1061 if prot.type_of() != SEXPTYPE::VECSXP || prot.len() < PROT_VEC_LEN as usize {
1062 return None;
1063 }
1064 let stored_sym = prot.vector_elt(PROT_TYPE_ID_INDEX);
1065 if stored_sym.type_of() != SEXPTYPE::SYMSXP {
1066 return None;
1067 }
1068 Some(symbol_name(stored_sym))
1069 }
1070 }
1071 // endregion
1072}
1073
1074impl ExternalPtr<()> {
1075 /// Create a type-erased ExternalPtr from an EXTPTRSXP without checking the stored type.
1076 ///
1077 /// # Safety
1078 ///
1079 /// - `sexp` must be a valid EXTPTRSXP
1080 /// - Caller must ensure exclusive ownership semantics are upheld
1081 #[inline]
1082 pub unsafe fn from_sexp(sexp: SEXP) -> Self {
1083 debug_assert!(sexp.type_of() == SEXPTYPE::EXTPTRSXP);
1084 unsafe { Self::from_sexp_unchecked(sexp) }
1085 }
1086
1087 /// Check whether the stored `Box<dyn Any>` contains a `T`.
1088 ///
1089 /// Uses `Any::is` for authoritative runtime type checking.
1090 #[inline]
1091 pub fn is<T: TypedExternal>(&self) -> bool {
1092 let any_raw = unsafe { R_ExternalPtrAddr(self.sexp) as *mut Box<dyn Any> };
1093 if any_raw.is_null() {
1094 return false;
1095 }
1096 let any_box: &Box<dyn Any> = unsafe { &*any_raw };
1097 any_box.is::<T>()
1098 }
1099
1100 /// Downcast to an immutable reference of the stored type if it matches `T`.
1101 ///
1102 /// Uses `Any::downcast_ref` for authoritative runtime type checking.
1103 #[inline]
1104 pub fn downcast_ref<T: TypedExternal>(&self) -> Option<&T> {
1105 let any_raw = unsafe { R_ExternalPtrAddr(self.sexp) as *mut Box<dyn Any> };
1106 if any_raw.is_null() {
1107 return None;
1108 }
1109 let any_box: &Box<dyn Any> = unsafe { &*any_raw };
1110 any_box.downcast_ref::<T>()
1111 }
1112
1113 /// Downcast to a mutable reference of the stored type if it matches `T`.
1114 ///
1115 /// Uses `Any::downcast_mut` for authoritative runtime type checking.
1116 #[inline]
1117 pub fn downcast_mut<T: TypedExternal>(&mut self) -> Option<&mut T> {
1118 let any_raw = unsafe { R_ExternalPtrAddr(self.sexp) as *mut Box<dyn Any> };
1119 if any_raw.is_null() {
1120 return None;
1121 }
1122 let any_box: &mut Box<dyn Any> = unsafe { &mut *any_raw };
1123 any_box.downcast_mut::<T>()
1124 }
1125}
1126
1127/// Error returned when type checking fails in `try_from_sexp_with_error`.
1128///
1129/// The `found` field in `Mismatch` contains a `&'static str` from R's
1130/// interned symbol table, which persists for the R session lifetime.
1131#[derive(Debug, Clone)]
1132pub enum TypeMismatchError {
1133 /// The external pointer's address was null.
1134 NullPointer,
1135 /// The prot slot didn't contain a valid type symbol.
1136 InvalidTypeId,
1137 /// The stored type doesn't match the expected type.
1138 Mismatch {
1139 /// Expected Rust type name from this pointer wrapper.
1140 expected: &'static str,
1141 /// Actual stored Rust type name found in pointer metadata.
1142 found: &'static str,
1143 },
1144}
1145
1146impl fmt::Display for TypeMismatchError {
1147 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1148 match self {
1149 Self::NullPointer => write!(f, "external pointer is null"),
1150 Self::InvalidTypeId => write!(f, "external pointer has no valid type id"),
1151 Self::Mismatch { expected, found } => {
1152 write!(
1153 f,
1154 "type mismatch: expected `{}`, found `{}`",
1155 expected, found
1156 )
1157 }
1158 }
1159 }
1160}
1161
1162impl std::error::Error for TypeMismatchError {}
1163// endregion
1164
1165// region: MaybeUninit support
1166
1167// We need a separate TypedExternal impl for MaybeUninit<T>
1168// This is typically done via blanket impl or macro
1169
1170impl<T: TypedExternal> ExternalPtr<MaybeUninit<T>>
1171where
1172 MaybeUninit<T>: TypedExternal,
1173{
1174 /// Converts to `ExternalPtr<T>`.
1175 ///
1176 /// # Safety
1177 ///
1178 /// The value must have been initialized.
1179 ///
1180 /// # Implementation Note
1181 ///
1182 /// This method creates a *new* SEXP with `T`'s type information, leaving
1183 /// the original `MaybeUninit<T>` SEXP as an orphaned empty shell in R's heap.
1184 /// This is necessary because the type ID stored in the prot slot must match
1185 /// the actual type. The orphaned SEXP will be cleaned up by R's GC eventually.
1186 ///
1187 /// If you need to avoid this overhead, consider using `ExternalPtr<T>::new`
1188 /// directly and initializing in place via `as_mut`.
1189 ///
1190 /// Equivalent to `Box::assume_init`.
1191 #[inline]
1192 pub fn assume_init(self) -> ExternalPtr<T> {
1193 // Get the raw pointer (this clears the original SEXP, making its finalizer a no-op)
1194 let ptr = Self::into_raw(self).cast();
1195
1196 // Create a new ExternalPtr with T's type info
1197 unsafe { ExternalPtr::from_raw(ptr) }
1198 }
1199
1200 /// Writes a value and converts to initialized.
1201 ///
1202 /// Creates a new SEXP with `T`'s type information (the original
1203 /// `MaybeUninit<T>` SEXP becomes an orphaned shell, cleaned up by GC).
1204 #[inline]
1205 pub fn write(mut self, value: T) -> ExternalPtr<T> {
1206 unsafe {
1207 (*Self::as_mut_ptr(&mut self)).write(value);
1208 self.assume_init()
1209 }
1210 }
1211}
1212/// Type-erased `ExternalPtr` for cases where the concrete `T` is not needed.
1213pub type ErasedExternalPtr = ExternalPtr<()>;
1214// endregion
1215
1216// region: Trait Implementations
1217
1218impl<T: TypedExternal> Deref for ExternalPtr<T> {
1219 type Target = T;
1220
1221 #[inline]
1222 fn deref(&self) -> &T {
1223 Self::as_ref(self).expect("ExternalPtr is null or cleared")
1224 }
1225}
1226
1227impl<T: TypedExternal> DerefMut for ExternalPtr<T> {
1228 #[inline]
1229 fn deref_mut(&mut self) -> &mut T {
1230 Self::as_mut(self).expect("ExternalPtr is null or cleared")
1231 }
1232}
1233
1234impl<T: TypedExternal> AsRef<T> for ExternalPtr<T> {
1235 #[inline]
1236 fn as_ref(&self) -> &T {
1237 Self::as_ref(self).expect("ExternalPtr is null or cleared")
1238 }
1239}
1240
1241impl<T: TypedExternal> AsMut<T> for ExternalPtr<T> {
1242 #[inline]
1243 fn as_mut(&mut self) -> &mut T {
1244 Self::as_mut(self).expect("ExternalPtr is null or cleared")
1245 }
1246}
1247
1248impl<T: TypedExternal> std::borrow::Borrow<T> for ExternalPtr<T> {
1249 #[inline]
1250 fn borrow(&self) -> &T {
1251 Self::as_ref(self).expect("ExternalPtr is null or cleared")
1252 }
1253}
1254
1255impl<T: TypedExternal> std::borrow::BorrowMut<T> for ExternalPtr<T> {
1256 #[inline]
1257 fn borrow_mut(&mut self) -> &mut T {
1258 Self::as_mut(self).expect("ExternalPtr is null or cleared")
1259 }
1260}
1261
1262impl<T: TypedExternal + Clone> Clone for ExternalPtr<T> {
1263 /// Deep clones the inner value into a new ExternalPtr.
1264 ///
1265 /// This creates a completely independent ExternalPtr with its own
1266 /// heap allocation and finalizer.
1267 #[inline]
1268 fn clone(&self) -> Self {
1269 Self::new((**self).clone())
1270 }
1271
1272 #[inline]
1273 fn clone_from(&mut self, source: &Self) {
1274 (**self).clone_from(&**source);
1275 }
1276}
1277
1278impl<T: TypedExternal + Default> Default for ExternalPtr<T> {
1279 /// Creates an ExternalPtr containing the default value of T.
1280 #[inline]
1281 fn default() -> Self {
1282 Self::new(T::default())
1283 }
1284}
1285
1286impl<T: TypedExternal + fmt::Debug> fmt::Debug for ExternalPtr<T> {
1287 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1288 fmt::Debug::fmt(&**self, f)
1289 }
1290}
1291
1292impl<T: TypedExternal + fmt::Display> fmt::Display for ExternalPtr<T> {
1293 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1294 fmt::Display::fmt(&**self, f)
1295 }
1296}
1297
1298impl<T: TypedExternal> fmt::Pointer for ExternalPtr<T> {
1299 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1300 fmt::Pointer::fmt(&Self::as_ptr(self), f)
1301 }
1302}
1303
1304impl<T: TypedExternal + PartialEq> PartialEq for ExternalPtr<T> {
1305 #[inline]
1306 fn eq(&self, other: &Self) -> bool {
1307 **self == **other
1308 }
1309}
1310
1311impl<T: TypedExternal + Eq> Eq for ExternalPtr<T> {}
1312
1313impl<T: TypedExternal + PartialOrd> PartialOrd for ExternalPtr<T> {
1314 #[inline]
1315 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1316 (**self).partial_cmp(&**other)
1317 }
1318}
1319
1320impl<T: TypedExternal + Ord> Ord for ExternalPtr<T> {
1321 #[inline]
1322 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1323 (**self).cmp(&**other)
1324 }
1325}
1326
1327impl<T: TypedExternal + Hash> Hash for ExternalPtr<T> {
1328 #[inline]
1329 fn hash<H: Hasher>(&self, state: &mut H) {
1330 (**self).hash(state);
1331 }
1332}
1333
1334impl<T: TypedExternal + std::iter::Iterator> std::iter::Iterator for ExternalPtr<T> {
1335 type Item = T::Item;
1336
1337 fn next(&mut self) -> Option<Self::Item> {
1338 (**self).next()
1339 }
1340
1341 fn size_hint(&self) -> (usize, Option<usize>) {
1342 (**self).size_hint()
1343 }
1344
1345 fn nth(&mut self, n: usize) -> Option<Self::Item> {
1346 (**self).nth(n)
1347 }
1348}
1349
1350impl<T: TypedExternal + std::iter::DoubleEndedIterator> std::iter::DoubleEndedIterator
1351 for ExternalPtr<T>
1352{
1353 fn next_back(&mut self) -> Option<Self::Item> {
1354 (**self).next_back()
1355 }
1356
1357 fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
1358 (**self).nth_back(n)
1359 }
1360}
1361
1362impl<T: TypedExternal + std::iter::ExactSizeIterator> std::iter::ExactSizeIterator
1363 for ExternalPtr<T>
1364{
1365 fn len(&self) -> usize {
1366 (**self).len()
1367 }
1368}
1369
1370impl<T: TypedExternal + std::iter::FusedIterator> std::iter::FusedIterator for ExternalPtr<T> {}
1371
1372impl<T: TypedExternal> From<T> for ExternalPtr<T> {
1373 #[inline]
1374 fn from(t: T) -> Self {
1375 Self::new(t)
1376 }
1377}
1378
1379impl<T: TypedExternal> From<Box<T>> for ExternalPtr<T> {
1380 #[inline]
1381 fn from(boxed: Box<T>) -> Self {
1382 unsafe { Self::from_raw(Box::into_raw(boxed)) }
1383 }
1384}
1385
1386// Note: We intentionally don't implement Drop for ExternalPtr.
1387// The R finalizer handles cleanup. If you need deterministic cleanup,
1388// use `into_raw` and manage it yourself.
1389
1390// However, we need to think about what happens if the ExternalPtr is dropped
1391// in Rust without going through R's GC. This is a design decision.
1392
1393// Option 1: No-op Drop (current) - R GC handles it eventually
1394// Option 2: Clear the pointer and let the value leak
1395// Option 3: Actually drop the value now
1396
1397// For now, we implement a no-op. R will clean up.
1398impl<T: TypedExternal> Drop for ExternalPtr<T> {
1399 fn drop(&mut self) {
1400 // The finalizer registered with R will handle cleanup.
1401 // We don't do anything here to avoid double-free.
1402 //
1403 // If you want deterministic cleanup, use:
1404 // let _ = ExternalPtr::into_inner(ptr);
1405 // or
1406 // drop(unsafe { Box::from_raw(ExternalPtr::into_raw(ptr)) });
1407 }
1408}
1409// endregion
1410
1411// region: Finalizer
1412
1413/// Non-generic C finalizer called by R's garbage collector.
1414///
1415/// Since `ExternalPtr` stores `Box<Box<dyn Any>>`, the `Any` vtable carries
1416/// the concrete type's drop function. No generic parameter needed — one
1417/// finalizer function handles all `ExternalPtr<T>` types.
1418extern "C-unwind" fn release_any(sexp: SEXP) {
1419 if sexp.is_null() {
1420 return;
1421 }
1422 if sexp.is_nil() {
1423 return;
1424 }
1425
1426 let any_raw = unsafe { R_ExternalPtrAddr(sexp) as *mut Box<dyn Any> };
1427
1428 // Guard against double-finalization
1429 if any_raw.is_null() {
1430 return;
1431 }
1432
1433 // Clear the external pointer first (prevents double-free if called again)
1434 unsafe { R_ClearExternalPtr(sexp) };
1435
1436 // Reconstruct the outer Box<Box<dyn Any>> and let it drop.
1437 // This drops the outer Box, then the inner Box<dyn Any>, which
1438 // uses the vtable to drop the concrete T value.
1439 drop(unsafe { Box::from_raw(any_raw) });
1440}
1441// endregion
1442
1443// region: Utility: ExternalSlice (helper for slice data)
1444
1445/// A slice stored as a standalone struct, suitable for wrapping in ExternalPtr.
1446///
1447/// This is analogous to the data inside a `Box<[T]>`, but stores capacity
1448/// for proper deallocation when created from a `Vec`.
1449///
1450/// # Usage
1451///
1452/// To use with `ExternalPtr`, implement `TypedExternal` for your specific
1453/// `ExternalSlice<YourType>`:
1454///
1455/// ```ignore
1456/// impl_typed_external!(ExternalSlice<MyElement>);
1457/// let ptr = ExternalPtr::new(ExternalSlice::new(vec![1, 2, 3]));
1458/// ```
1459#[repr(C)]
1460pub struct ExternalSlice<T: 'static> {
1461 ptr: NonNull<T>,
1462 len: usize,
1463 capacity: usize,
1464}
1465
1466impl<T: 'static> ExternalSlice<T> {
1467 /// Create an external slice from a `Vec`, preserving its allocation.
1468 pub fn new(slice: Vec<T>) -> Self {
1469 let mut vec = ManuallyDrop::new(slice);
1470 Self {
1471 ptr: unsafe { NonNull::new_unchecked(vec.as_mut_ptr()) },
1472 len: vec.len(),
1473 capacity: vec.capacity(),
1474 }
1475 }
1476
1477 /// Create from a boxed slice (capacity == len).
1478 pub fn from_boxed(boxed: Box<[T]>) -> Self {
1479 let len = boxed.len();
1480 let ptr = Box::into_raw(boxed).cast();
1481 Self {
1482 ptr: unsafe { NonNull::new_unchecked(ptr) },
1483 len,
1484 capacity: len,
1485 }
1486 }
1487
1488 /// Borrow the contents as a shared slice.
1489 pub fn as_slice(&self) -> &[T] {
1490 unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
1491 }
1492
1493 /// Borrow the contents as a mutable slice.
1494 pub fn as_mut_slice(&mut self) -> &mut [T] {
1495 unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
1496 }
1497
1498 /// Number of elements in the slice.
1499 pub fn len(&self) -> usize {
1500 self.len
1501 }
1502
1503 /// Returns true if the slice is empty.
1504 pub fn is_empty(&self) -> bool {
1505 self.len == 0
1506 }
1507
1508 /// Capacity of the underlying allocation.
1509 pub fn capacity(&self) -> usize {
1510 self.capacity
1511 }
1512}
1513
1514impl<T: 'static> Drop for ExternalSlice<T> {
1515 fn drop(&mut self) {
1516 unsafe {
1517 let _ = Vec::from_raw_parts(self.ptr.as_ptr(), self.len, self.capacity);
1518 }
1519 }
1520}
1521// endregion
1522
1523mod altrep_helpers;
1524pub use altrep_helpers::*;