diff --git a/newsfragments/3871.added.md b/newsfragments/3871.added.md new file mode 100644 index 00000000..f90e92fd --- /dev/null +++ b/newsfragments/3871.added.md @@ -0,0 +1 @@ +Add `Py::drop_ref` to explicitly drop a `Py`` and immediately decrease the Python reference count if the GIL is already held. diff --git a/src/instance.rs b/src/instance.rs index 3f19637e..8d57bb9e 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -822,6 +822,10 @@ impl IntoPy for Borrowed<'_, '_, T> { /// Otherwise, the reference count will be decreased the next time the GIL is /// reacquired. /// +/// If you happen to be already holding the GIL, [`Py::drop_ref`] will decrease +/// the Python reference count immediately and will execute slightly faster than +/// relying on implicit [`Drop`]s. +/// /// # A note on `Send` and `Sync` /// /// Accessing this object is threadsafe, since any access to its API requires a [`Python<'py>`](crate::Python) token. @@ -1228,6 +1232,35 @@ impl Py { unsafe { Py::from_borrowed_ptr(py, self.0.as_ptr()) } } + /// Drops `self` and immediately decreases its reference count. + /// + /// This method is a micro-optimisation over [`Drop`] if you happen to be holding the GIL + /// already. + /// + /// Note that if you are using [`Bound`], you do not need to use [`Self::drop_ref`] since + /// [`Bound`] guarantees that the GIL is held. + /// + /// # Examples + /// + /// ```rust + /// use pyo3::prelude::*; + /// use pyo3::types::PyDict; + /// + /// # fn main() { + /// Python::with_gil(|py| { + /// let object: Py = PyDict::new_bound(py).unbind(); + /// + /// // some usage of object + /// + /// object.drop_ref(py); + /// }); + /// # } + /// ``` + #[inline] + pub fn drop_ref(self, py: Python<'_>) { + let _ = self.into_bound(py); + } + /// Returns whether the object is considered to be None. /// /// This is equivalent to the Python expression `self is None`. @@ -2142,6 +2175,23 @@ a = A() }) } + #[test] + fn explicit_drop_ref() { + Python::with_gil(|py| { + let object: Py = PyDict::new_bound(py).unbind(); + let object2 = object.clone_ref(py); + + assert_eq!(object.as_ptr(), object2.as_ptr()); + assert_eq!(object.get_refcnt(py), 2); + + object.drop_ref(py); + + assert_eq!(object2.get_refcnt(py), 1); + + object2.drop_ref(py); + }); + } + #[cfg(feature = "macros")] mod using_macros { use crate::PyCell;