extendr_api/wrapper/
externalptr.rs

1//! `ExternalPtr` is a way to leak Rust allocated data to R, forego deallocation
2//! to R and its GC strategy.
3//!
4//! An `ExternalPtr` encompasses three values, an owned pointer to the Rust
5//! type, a `tag` and a `prot`. Tag is a helpful naming of the type, but
6//! it doesn't offer any solid type-checking capability. And `prot` is meant
7//! to be R values, that are supposed to be kept together with the `ExternalPtr`.
8//!
9//! Neither `tag` nor `prot` are attributes, therefore to use `ExternalPtr` as
10//! a class in R, you must decorate it with a class-attribute manually.
11//!
12//! **Beware**: Equality (by way of `PartialEq`) does not imply equality of value,
13//! but equality of pointer. Two objects stored as `ExternalPtr` may be equal
14//! in value, but be two distinct entities, with distinct pointers.
15//!
16//! Instead, rely on `AsRef` to make _by value_ comparison, e.g. to compare
17//! for equality of
18//! two instances of `ExternalPtr<T>` by value, `a.as_ref() == b.as_ref()`.
19//!
20use super::*;
21use extendr_ffi::{
22    R_ClearExternalPtr, R_ExternalPtrAddr, R_ExternalPtrProtected, R_ExternalPtrTag,
23    R_MakeExternalPtr, R_NilValue, R_SetExternalPtrTag,
24};
25use std::{any::Any, fmt::Debug};
26/// Wrapper for creating R objects containing any Rust object.
27///
28/// ```
29/// use extendr_api::prelude::*;
30/// test! {
31///     let extptr = ExternalPtr::new(1);
32///     assert_eq!(*extptr, 1);
33///     let robj : Robj = extptr.into();
34///     let extptr2 : ExternalPtr<i32> = robj.try_into().unwrap();
35///     assert_eq!(*extptr2, 1);
36/// }
37/// ```
38#[repr(transparent)]
39pub struct ExternalPtr<T> {
40    /// This is the contained Robj.
41    pub(crate) robj: Robj,
42
43    /// This is a zero-length object that holds the type of the object.
44    _marker: std::marker::PhantomData<T>,
45}
46
47/// Manual implementation of `PartialEq`, because the constraint `T: PartialEq`
48/// is not necessary.
49impl<T> PartialEq for ExternalPtr<T> {
50    fn eq(&self, other: &Self) -> bool {
51        self.robj == other.robj && self._marker == other._marker
52    }
53}
54
55/// Manual implementation of `Clone` trait, because the assumed constraint `T: Clone` is not necessary.
56impl<T> Clone for ExternalPtr<T> {
57    fn clone(&self) -> Self {
58        Self {
59            robj: self.robj.clone(),
60            _marker: self._marker,
61        }
62    }
63}
64
65impl<T> robj::GetSexp for ExternalPtr<T> {
66    unsafe fn get(&self) -> SEXP {
67        self.robj.get()
68    }
69
70    unsafe fn get_mut(&mut self) -> SEXP {
71        self.robj.get_mut()
72    }
73
74    /// Get a reference to a Robj for this type.
75    fn as_robj(&self) -> &Robj {
76        &self.robj
77    }
78
79    /// Get a mutable reference to a Robj for this type.
80    fn as_robj_mut(&mut self) -> &mut Robj {
81        &mut self.robj
82    }
83}
84
85/// len() and is_empty()
86impl<T> Length for ExternalPtr<T> {}
87
88/// rtype() and rany()
89impl<T> Types for ExternalPtr<T> {}
90
91/// `set_attrib`
92impl<T> Attributes for ExternalPtr<T> {}
93
94/// as_*()
95impl<T> Conversions for ExternalPtr<T> {}
96
97/// find_var() etc.
98impl<T> Rinternals for ExternalPtr<T> {}
99
100/// as_typed_slice_raw() etc.
101impl<T> Slices for ExternalPtr<T> {}
102
103/// dollar() etc.
104impl<T> Operators for ExternalPtr<T> {}
105
106impl<T: 'static> Deref for ExternalPtr<T> {
107    type Target = T;
108
109    /// This allows us to treat the Robj as if it is the type T.
110    fn deref(&self) -> &Self::Target {
111        self.addr()
112    }
113}
114
115impl<T: 'static> DerefMut for ExternalPtr<T> {
116    /// This allows us to treat the Robj as if it is the mutable type T.
117    fn deref_mut(&mut self) -> &mut Self::Target {
118        self.addr_mut()
119    }
120}
121
122impl<T: 'static> ExternalPtr<T> {
123    /// Construct an external pointer object from any type T.
124    /// In this case, the R object owns the data and will drop the Rust object
125    /// when the last reference is removed via register_c_finalizer.
126    ///
127    /// An ExternalPtr behaves like a Box except that the information is
128    /// tracked by a R object.
129    pub fn new(val: T) -> Self {
130        single_threaded(|| unsafe {
131            // This allocates some memory for our object and moves the object into it.
132            let boxed: Box<dyn Any> = Box::new(val);
133            let boxed: Box<Box<dyn Any>> = Box::new(boxed);
134
135            // This constructs an external pointer to our boxed data.
136            // into_raw() converts the box to a malloced pointer.
137            let robj = {
138                let boxed_ptr = Box::into_raw(boxed);
139                let prot = Robj::from(());
140                let type_name: Robj = std::any::type_name::<T>().into();
141
142                Robj::from_sexp(single_threaded(|| {
143                    R_MakeExternalPtr(boxed_ptr.cast(), type_name.get(), prot.get())
144                }))
145            };
146
147            extern "C" fn finalizer(x: SEXP) {
148                unsafe {
149                    let ptr = R_ExternalPtrAddr(x).cast::<Box<dyn Any>>();
150
151                    // Free the `tag`, which is the type-name
152                    R_SetExternalPtrTag(x, R_NilValue);
153
154                    // Convert the pointer to a box and drop it implictly.
155                    // This frees up the memory we have used and calls the "T::drop" method if there is one.
156                    drop(Box::from_raw(ptr));
157
158                    // Now set the pointer in ExternalPTR to C `NULL`
159                    R_ClearExternalPtr(x);
160                }
161            }
162
163            // Tell R about our finalizer
164            robj.register_c_finalizer(Some(finalizer));
165
166            // Return an object in a wrapper.
167            Self {
168                robj,
169                _marker: std::marker::PhantomData,
170            }
171        })
172    }
173
174    // TODO: make a constructor for references?
175
176    /// Get the "tag" of an external pointer. This is the type name in the common case.
177    pub fn tag(&self) -> Robj {
178        unsafe { Robj::from_sexp(R_ExternalPtrTag(self.robj.get())) }
179    }
180
181    /// Get the "protected" field of an external pointer. This is NULL in the common case.
182    pub fn protected(&self) -> Robj {
183        unsafe { Robj::from_sexp(R_ExternalPtrProtected(self.robj.get())) }
184    }
185
186    /// Get the "address" field of an external pointer.
187    /// Normally, we will use Deref to do this.
188    ///
189    /// ## Panics
190    ///
191    /// When the underlying pointer is C `NULL`.
192    pub fn addr(&self) -> &T {
193        self.try_addr().unwrap()
194    }
195
196    /// Get the "address" field of an external pointer as a mutable reference.
197    /// Normally, we will use DerefMut to do this.
198    ///
199    /// ## Panics
200    ///
201    /// When the underlying pointer is C `NULL`.
202    pub fn addr_mut(&mut self) -> &mut T {
203        self.try_addr_mut().unwrap()
204    }
205
206    /// Get the "address" field of an external pointer.
207    /// Normally, we will use Deref to do this.
208    ///
209    /// ## Panics
210    ///
211    /// When the underlying pointer is C `NULL`.
212    pub fn try_addr(&self) -> Result<&T> {
213        unsafe {
214            R_ExternalPtrAddr(self.robj.get())
215                .cast::<Box<dyn Any>>()
216                .as_ref()
217                .ok_or_else(|| Error::ExpectedExternalNonNullPtr(self.robj.clone()))
218                .map(|x| x.downcast_ref::<T>().unwrap())
219        }
220    }
221
222    /// Get the "address" field of an external pointer as a mutable reference.
223    /// Normally, we will use DerefMut to do this.
224    ///
225    /// ## Panics
226    ///
227    /// When the underlying pointer is C `NULL`.
228    pub fn try_addr_mut(&mut self) -> Result<&mut T> {
229        unsafe {
230            R_ExternalPtrAddr(self.robj.get_mut())
231                .cast::<Box<dyn Any>>()
232                .as_mut()
233                .ok_or_else(|| Error::ExpectedExternalNonNullPtr(self.robj.clone()))
234                .map(|x| x.downcast_mut::<T>().unwrap())
235        }
236    }
237}
238
239impl<T: 'static> TryFrom<&Robj> for &ExternalPtr<T> {
240    type Error = Error;
241
242    fn try_from(value: &Robj) -> Result<Self> {
243        if !value.is_external_pointer() {
244            return Err(Error::ExpectedExternalPtr(value.clone()));
245        }
246
247        // check type by downcasting
248        let boxed_ptr = unsafe {
249            value
250                .external_ptr_addr::<Box<dyn Any>>()
251                .cast_const()
252                .as_ref()
253                .ok_or_else(|| Error::ExpectedExternalNonNullPtr(value.clone()))?
254        };
255
256        if boxed_ptr.downcast_ref::<T>().is_none() {
257            return Err(Error::ExpectedExternalPtrType(
258                value.clone(),
259                std::any::type_name::<T>().to_string(),
260            ));
261        }
262
263        unsafe { Ok(std::mem::transmute::<&Robj, &ExternalPtr<T>>(value)) }
264    }
265}
266
267impl<T: 'static> TryFrom<&mut Robj> for &mut ExternalPtr<T> {
268    type Error = Error;
269
270    fn try_from(value: &mut Robj) -> Result<Self> {
271        if !value.is_external_pointer() {
272            return Err(Error::ExpectedExternalPtr(value.clone()));
273        }
274
275        // check type by downcasting
276        let boxed_ptr = unsafe {
277            value
278                .external_ptr_addr::<Box<dyn Any>>()
279                .cast_const()
280                .as_ref()
281                .ok_or_else(|| Error::ExpectedExternalNonNullPtr(value.clone()))?
282        };
283
284        if boxed_ptr.downcast_ref::<T>().is_none() {
285            return Err(Error::ExpectedExternalPtrType(
286                value.clone(),
287                std::any::type_name::<T>().to_string(),
288            ));
289        }
290
291        unsafe { Ok(std::mem::transmute::<&mut Robj, &mut ExternalPtr<T>>(value)) }
292    }
293}
294
295impl<T: 'static> TryFrom<&Robj> for ExternalPtr<T> {
296    type Error = Error;
297
298    fn try_from(robj: &Robj) -> Result<Self> {
299        let result: &Self = robj.try_into()?;
300        Ok(result.clone())
301    }
302}
303
304impl<T: 'static> TryFrom<Robj> for ExternalPtr<T> {
305    type Error = Error;
306
307    fn try_from(robj: Robj) -> Result<Self> {
308        <ExternalPtr<T>>::try_from(&robj)
309    }
310}
311
312impl<T> From<ExternalPtr<T>> for Robj {
313    fn from(val: ExternalPtr<T>) -> Self {
314        val.robj
315    }
316}
317
318impl<T> From<Option<ExternalPtr<T>>> for Robj {
319    fn from(value: Option<ExternalPtr<T>>) -> Self {
320        match value {
321            None => nil_value(),
322            Some(value) => value.into(),
323        }
324    }
325}
326
327impl<T: Debug + 'static> std::fmt::Debug for ExternalPtr<T> {
328    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
329        (&**self as &T).fmt(f)
330    }
331}
332
333impl<T: 'static> AsRef<T> for ExternalPtr<T> {
334    fn as_ref(&self) -> &T {
335        self.addr()
336    }
337}
338
339impl<T: 'static> AsMut<T> for ExternalPtr<T> {
340    fn as_mut(&mut self) -> &mut T {
341        self.addr_mut()
342    }
343}
344
345#[cfg(test)]
346mod tests {
347    use std::error::Error;
348
349    use super::*;
350    use extendr_engine::with_r;
351
352    #[derive(Debug)]
353    struct BareWrapper(i32);
354
355    #[test]
356    fn externalptr_is_ptr() -> std::result::Result<(), Box<dyn Error>> {
357        with_r(|| {
358            let a = BareWrapper(42);
359            let b = BareWrapper(42);
360            assert_eq!(a.0, b.0);
361
362            let a_ptr = std::ptr::addr_of!(a);
363            let b_ptr = std::ptr::addr_of!(b);
364            let a_externalptr = ExternalPtr::new(a);
365            let b_externalptr = ExternalPtr::new(b);
366
367            assert_ne!(
368                a_ptr, b_ptr,
369                "pointers has to be equal by address, not value"
370            );
371
372            assert_ne!(
373                a_externalptr.robj, b_externalptr.robj,
374                "R only knows about the pointer, and not the pointee"
375            );
376            assert_ne!(
377                a_externalptr, b_externalptr,
378                "ExternalPtr acts exactly like a pointer"
379            );
380            assert_ne!(&a_externalptr, &b_externalptr,);
381            Ok(())
382        })
383    }
384
385    #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
386    struct Wrapper(i32);
387
388    #[test]
389    fn compare_externalptr_pointee() -> std::result::Result<(), Box<dyn Error>> {
390        with_r(|| {
391            let a = Wrapper(42);
392            let b = Wrapper(42);
393            let a_externalptr = ExternalPtr::new(a);
394            let b_externalptr = ExternalPtr::new(b);
395            assert_eq!(a_externalptr.as_ref(), b_externalptr.as_ref());
396
397            // let's test more use of `PartialOrd` on `T`
398            let a_externalptr = ExternalPtr::new(Wrapper(50));
399            let b_externalptr = ExternalPtr::new(Wrapper(60));
400            assert!(a_externalptr.as_ref() <= b_externalptr.as_ref());
401            assert_eq!(
402                a_externalptr.as_ref().max(b_externalptr.as_ref()),
403                &Wrapper(60)
404            );
405            Ok(())
406        })
407    }
408}