Expand conversions documentation

This commit is contained in:
mejrs 2022-05-03 00:46:53 +02:00
parent dce4377eb4
commit f34b92a368
1 changed files with 93 additions and 20 deletions

View File

@ -10,30 +10,88 @@ use crate::{
}; };
use std::ptr::NonNull; use std::ptr::NonNull;
/// This trait represents that **we can do zero-cost conversion from the object /// Returns a borrowed pointer to a Python object.
/// to a FFI pointer**.
/// ///
/// 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 /// # Examples
/// ///
/// ``` /// ```rust
/// use pyo3::{prelude::*, AsPyPointer}; /// use pyo3::prelude::*;
/// use pyo3::AsPyPointer;
/// use pyo3::types::PyString;
/// use pyo3::ffi;
///
/// Python::with_gil(|py| { /// Python::with_gil(|py| {
/// let dict = pyo3::types::PyDict::new(py); /// let s: Py<PyString> = "foo".into_py(py);
/// // All native object wrappers implement AsPyPointer!!! /// let ptr = s.as_ptr();
/// assert_ne!(dict.as_ptr(), std::ptr::null_mut()); ///
/// 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 { 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; 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<PyString> = "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 { pub trait IntoPyPointer {
/// Retrieves the underlying FFI pointer. Whether pointer owned or borrowed /// Returns the underlying FFI pointer as an owned pointer.
/// depends on implementation. ///
/// If `self` has ownership of the underlying pointer, it will "steal" ownership of it.
fn into_ptr(self) -> *mut ffi::PyObject; fn into_ptr(self) -> *mut ffi::PyObject;
} }
@ -188,17 +246,32 @@ pub trait IntoPy<T>: Sized {
fn into_py(self, py: Python<'_>) -> T; fn into_py(self, py: Python<'_>) -> T;
} }
/// `FromPyObject` is implemented by various types that can be extracted from /// Extract a type from a Python object.
/// a Python object reference.
/// ///
/// Normal usage is through the helper methods `Py::extract` or `PyAny::extract`:
/// ///
/// ```rust,ignore /// Normal usage is through the `extract` methods on [`Py`] and [`PyAny`], which forward to this trait.
/// let obj: Py<PyAny> = ...;
/// let value: &TargetType = obj.extract(py)?;
/// ///
/// let any: &PyAny = ...; /// # Examples
/// let value: &TargetType = any.extract()?; ///
/// ```rust
/// use pyo3::prelude::*;
/// use pyo3::types::PyString;
///
/// # fn main() -> PyResult<()> {
/// Python::with_gil(|py| {
/// let obj: Py<PyString> = 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 /// Note: depending on the implementation, the lifetime of the extracted result may