Skip to main content

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::*;