diff rust/pyo3-sharedref/src/lib.rs @ 52637:a945845137b1

rust-pyo3-sharedref: share/map methods on PyShareable The pattern to borrow (with owner) for the sole purpose of calling `share_immutable`, and then `map()` is pretty common, being exactly what is needed in the constructors of derived Python objects. we therefore introduce new helpers doing exactly that. We keep the intermediate `share()` and `try_share()` that are used in integration tests, but is is possible that consumers of the API never need it. A nice feature of the new `share_map()` helper is that it collapses all the unsafety in constructors of derived object to one call. It makes sense, since the reason for unsafety is the same all along the call stack: the `owner` object, that cannot be guessed from `PyShareable` itself, must be the right one. It is remarkable that it is only at this point that the compiler insists that `T` should be `'static`, which is actually very reasonable, as it encompasses owned data. We could have set it since the beginning, but the added value is low, as PyO3 would not let us use some `PyShareable<&'a T>` as a Python object data field (with `'a` not outliving `'static` of course)
author Georges Racinet <georges.racinet@cloudcrane.io>
date Sun, 15 Dec 2024 15:36:11 +0100
parents 4a73eb3923ac
children 76a0bdb0e4ca
line wrap: on
line diff
--- a/rust/pyo3-sharedref/src/lib.rs	Sun Dec 15 15:19:43 2024 +0100
+++ b/rust/pyo3-sharedref/src/lib.rs	Sun Dec 15 15:36:11 2024 +0100
@@ -110,9 +110,7 @@
 ///     fn new(s: &Bound<'_, Set>) -> Self {
 ///         let py = s.py();
 ///         let rust_set = &s.borrow().rust_set;
-///         let shareable_ref = unsafe { rust_set.borrow_with_owner(s) };
-///         let shared_set = shareable_ref.share_immutable();
-///         let iter = unsafe { shared_set.map(py, |o| o.iter()) };
+///         let iter = unsafe { rust_set.share_map(s, |o| o.iter()) };
 ///         Self {
 ///             rust_iter: iter.into(),
 ///         }
@@ -177,7 +175,7 @@
     data: RwLock<T>,
 }
 
-impl<T> PyShareable<T> {
+impl<T: 'static> PyShareable<T> {
     /// Borrows the shared data and its state, keeping a reference
     /// on the owner Python object.
     ///
@@ -196,6 +194,57 @@
             data: &self.data,
         }
     }
+
+    /// Share for other Python objects
+    ///
+    /// # Safety
+    ///
+    /// The `data` must be owned by the `owner`. Otherwise, the resulting
+    /// [`SharedByPyObject`] would contain an invalid reference.
+    pub unsafe fn share<'py>(
+        &'py self,
+        owner: &'py Bound<'py, PyAny>,
+    ) -> SharedByPyObject<&'static T> {
+        self.borrow_with_owner(owner).share_immutable()
+    }
+
+    /// Share for other Python objects, transforming the inner data
+    /// with a closure
+    ///
+    /// # Safety
+    ///
+    /// The `data` must be owned by the `owner`. Otherwise, the resulting
+    /// [`SharedByPyObject`] would contain an invalid reference.
+    pub unsafe fn share_map<'py, U>(
+        &'py self,
+        owner: &'py Bound<'py, PyAny>,
+        f: impl FnOnce(&'static T) -> U,
+    ) -> SharedByPyObject<U> {
+        self.share(owner).map(owner.py(), f)
+    }
+
+    /// # Safety
+    ///
+    /// The `data` must be owned by the `owner`. Otherwise, the resulting
+    /// [`SharedByPyObject`] would contain an invalid reference.
+    pub unsafe fn try_share<'py>(
+        &'py self,
+        owner: &'py Bound<'py, PyAny>,
+    ) -> Result<SharedByPyObject<&'static T>, TryShareError> {
+        self.borrow_with_owner(owner).try_share_immutable()
+    }
+
+    /// # Safety
+    ///
+    /// The `data` must be owned by the `owner`. Otherwise, the resulting
+    /// [`SharedByPyObject`] would contain an invalid reference.
+    pub unsafe fn try_share_map<'py, U>(
+        &'py self,
+        owner: &'py Bound<'py, PyAny>,
+        f: impl FnOnce(&'static T) -> U,
+    ) -> Result<SharedByPyObject<U>, TryShareError> {
+        Ok(self.try_share(owner)?.map(owner.py(), f))
+    }
 }
 
 impl<T> From<T> for PyShareable<T> {