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(value: Robj) -> Result<Self> {
299        (&value).try_into()
300    }
301}
302
303impl<T: 'static> TryFrom<Robj> for &mut ExternalPtr<T> {
304    type Error = Error;
305
306    fn try_from(mut value: Robj) -> Result<Self> {
307        (&mut value).try_into()
308    }
309}
310
311impl<T: 'static> TryFrom<&Robj> for ExternalPtr<T> {
312    type Error = Error;
313
314    fn try_from(robj: &Robj) -> Result<Self> {
315        let result: &Self = robj.try_into()?;
316        Ok(result.clone())
317    }
318}
319
320impl<T: 'static> TryFrom<Robj> for ExternalPtr<T> {
321    type Error = Error;
322
323    fn try_from(robj: Robj) -> Result<Self> {
324        <ExternalPtr<T>>::try_from(&robj)
325    }
326}
327
328impl<T> From<ExternalPtr<T>> for Robj {
329    fn from(val: ExternalPtr<T>) -> Self {
330        val.robj
331    }
332}
333
334impl<T> From<Option<ExternalPtr<T>>> for Robj {
335    fn from(value: Option<ExternalPtr<T>>) -> Self {
336        match value {
337            None => nil_value(),
338            Some(value) => value.into(),
339        }
340    }
341}
342
343impl<T: Debug + 'static> std::fmt::Debug for ExternalPtr<T> {
344    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
345        (&**self as &T).fmt(f)
346    }
347}
348
349impl<T: 'static> AsRef<T> for ExternalPtr<T> {
350    fn as_ref(&self) -> &T {
351        self.addr()
352    }
353}
354
355impl<T: 'static> AsMut<T> for ExternalPtr<T> {
356    fn as_mut(&mut self) -> &mut T {
357        self.addr_mut()
358    }
359}
360
361#[cfg(test)]
362mod tests {
363    use super::*;
364    use extendr_engine::with_r;
365
366    #[derive(Debug)]
367    struct BareWrapper(i32);
368
369    #[test]
370    fn externalptr_is_ptr() {
371        with_r(|| {
372            let a = BareWrapper(42);
373            let b = BareWrapper(42);
374            assert_eq!(a.0, b.0);
375
376            let a_ptr = std::ptr::addr_of!(a);
377            let b_ptr = std::ptr::addr_of!(b);
378            let a_externalptr = ExternalPtr::new(a);
379            let b_externalptr = ExternalPtr::new(b);
380
381            assert_ne!(
382                a_ptr, b_ptr,
383                "pointers has to be equal by address, not value"
384            );
385
386            assert_ne!(
387                a_externalptr.robj, b_externalptr.robj,
388                "R only knows about the pointer, and not the pointee"
389            );
390            assert_ne!(
391                a_externalptr, b_externalptr,
392                "ExternalPtr acts exactly like a pointer"
393            );
394            assert_ne!(&a_externalptr, &b_externalptr,);
395        });
396    }
397
398    #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
399    struct Wrapper(i32);
400
401    #[test]
402    fn compare_externalptr_pointee() {
403        with_r(|| {
404            let a = Wrapper(42);
405            let b = Wrapper(42);
406            let a_externalptr = ExternalPtr::new(a);
407            let b_externalptr = ExternalPtr::new(b);
408            assert_eq!(a_externalptr.as_ref(), b_externalptr.as_ref());
409
410            // let's test more use of `PartialOrd` on `T`
411            let a_externalptr = ExternalPtr::new(Wrapper(50));
412            let b_externalptr = ExternalPtr::new(Wrapper(60));
413            assert!(a_externalptr.as_ref() <= b_externalptr.as_ref());
414            assert_eq!(
415                a_externalptr.as_ref().max(b_externalptr.as_ref()),
416                &Wrapper(60)
417            )
418        });
419    }
420}