diff --git a/src/conversion.rs b/src/conversion.rs index 8a005a26..3f4b5452 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -10,30 +10,88 @@ use crate::{ }; use std::ptr::NonNull; -/// This trait represents that **we can do zero-cost conversion from the object -/// to a FFI pointer**. +/// Returns a borrowed pointer to a Python object. /// -/// This trait is implemented for types that internally wrap a pointer to a Python object. +/// The returned pointer will be valid for as long as `self` is. It may be null depending on the +/// implementation. /// /// # Examples /// -/// ``` -/// use pyo3::{prelude::*, AsPyPointer}; +/// ```rust +/// use pyo3::prelude::*; +/// use pyo3::AsPyPointer; +/// use pyo3::types::PyString; +/// use pyo3::ffi; +/// /// Python::with_gil(|py| { -/// let dict = pyo3::types::PyDict::new(py); -/// // All native object wrappers implement AsPyPointer!!! -/// assert_ne!(dict.as_ptr(), std::ptr::null_mut()); +/// let s: Py = "foo".into_py(py); +/// let ptr = s.as_ptr(); +/// +/// let is_really_a_pystring = unsafe { ffi::PyUnicode_CheckExact(ptr) }; +/// assert_eq!(is_really_a_pystring, 1); /// }); /// ``` +/// +/// # Safety +/// +/// It is your responsibility to make sure that the underlying Python object is not dropped too +/// early. For example, the following code will cause undefined behavior: +/// +/// ```rust,no_run +/// # use pyo3::prelude::*; +/// # use pyo3::AsPyPointer; +/// # use pyo3::ffi; +/// # +/// Python::with_gil(|py| { +/// let ptr: *mut ffi::PyObject = 0xabad1dea_u32.into_py(py).as_ptr(); +/// +/// let isnt_a_pystring = unsafe { +/// // `ptr` is dangling, this is UB +/// ffi::PyUnicode_CheckExact(ptr) +/// }; +/// # assert_eq!(isnt_a_pystring, 0); +/// }); +/// ``` +/// +/// This happens because the pointer returned by `as_ptr` does not carry any lifetime information +/// and the Python object is dropped immediately after the `0xabad1dea_u32.into_py(py).as_ptr()` +/// expression is evaluated. To fix the problem, bind Python object to a local variable like earlier +/// to keep the Python object alive until the end of its scope. pub trait AsPyPointer { - /// Retrieves the underlying FFI pointer (as a borrowed pointer). + /// Returns the underlying FFI pointer as a borrowed pointer. fn as_ptr(&self) -> *mut ffi::PyObject; } -/// This trait allows retrieving the underlying FFI pointer from Python objects. +/// Returns an owned pointer to a Python object. +/// +/// The returned pointer will be valid until you decrease its reference count. It may be null +/// depending on the implementation. +/// It is your responsibility to decrease the reference count of the pointer to avoid leaking memory. +/// +/// # Examples +/// +/// ```rust +/// use pyo3::prelude::*; +/// use pyo3::IntoPyPointer; +/// use pyo3::types::PyString; +/// use pyo3::ffi; +/// +/// Python::with_gil(|py| { +/// let s: Py = "foo".into_py(py); +/// let ptr = s.into_ptr(); +/// +/// let is_really_a_pystring = unsafe { ffi::PyUnicode_CheckExact(ptr) }; +/// assert_eq!(is_really_a_pystring, 1); +/// +/// // Because we took ownership of the pointer, +/// // we must manually decrement it to avoid leaking memory +/// unsafe { ffi::Py_DECREF(ptr) }; +/// }); +/// ``` pub trait IntoPyPointer { - /// Retrieves the underlying FFI pointer. Whether pointer owned or borrowed - /// depends on implementation. + /// Returns the underlying FFI pointer as an owned pointer. + /// + /// If `self` has ownership of the underlying pointer, it will "steal" ownership of it. fn into_ptr(self) -> *mut ffi::PyObject; } @@ -188,17 +246,32 @@ pub trait IntoPy: Sized { fn into_py(self, py: Python<'_>) -> T; } -/// `FromPyObject` is implemented by various types that can be extracted from -/// a Python object reference. +/// Extract a type from a Python object. /// -/// Normal usage is through the helper methods `Py::extract` or `PyAny::extract`: /// -/// ```rust,ignore -/// let obj: Py = ...; -/// let value: &TargetType = obj.extract(py)?; +/// Normal usage is through the `extract` methods on [`Py`] and [`PyAny`], which forward to this trait. /// -/// let any: &PyAny = ...; -/// let value: &TargetType = any.extract()?; +/// # Examples +/// +/// ```rust +/// use pyo3::prelude::*; +/// use pyo3::types::PyString; +/// +/// # fn main() -> PyResult<()> { +/// Python::with_gil(|py| { +/// let obj: Py = PyString::new(py, "blah").into(); +/// +/// // Straight from an owned reference +/// let s: &str = obj.extract(py)?; +/// # assert_eq!(s, "blah"); +/// +/// // Or from a borrowed reference +/// let obj: &PyString = obj.as_ref(py); +/// let s: &str = obj.extract()?; +/// # assert_eq!(s, "blah"); +/// # Ok(()) +/// }) +/// # } /// ``` /// /// Note: depending on the implementation, the lifetime of the extracted result may