From 192c2c2b2cccd38f7373914b660da7c6531797f8 Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Sun, 6 Sep 2020 10:21:08 +0100 Subject: [PATCH] docs: add detail to wrapper type conversions --- guide/src/types.md | 162 +++++++++++++++++++++++++++++++++------------ 1 file changed, 118 insertions(+), 44 deletions(-) diff --git a/guide/src/types.md b/guide/src/types.md index 6f2d97e1b84..18ba0a1ad64 100644 --- a/guide/src/types.md +++ b/guide/src/types.md @@ -61,23 +61,54 @@ such as `getattr`, `setattr`, and `.call`. **Conversions:** +For a `&PyAny` object reference `any` where the underlying object is a Python-native type such as +a list: + ```rust # use pyo3::prelude::*; -# use pyo3::types::PyList; -# let gil = Python::acquire_gil(); -# let py = gil.python(); +# use pyo3::{Py, Python, PyAny, PyResult, types::PyList}; +# Python::with_gil(|py| -> PyResult<()> { let obj: &PyAny = PyList::empty(py); -// Convert to &ConcreteType using PyAny::downcast -let _: &PyList = obj.downcast().unwrap(); +// To &PyList with PyAny::downcast +let _: &PyList = obj.downcast()?; -// Convert to Py (aka PyObject) using .into() +// To Py (aka PyObject) with .into() let _: Py = obj.into(); -// Convert to Py using PyAny::extract -let _: Py = obj.extract().unwrap(); +// To Py with PyAny::extract +let _: Py = obj.extract()?; +# Ok(()) +# }).unwrap(); ``` +For a `&PyAny` object reference `any` where the underlying object is a `#[pyclass]`: + +```rust +# use pyo3::prelude::*; +# use pyo3::{Py, Python, PyAny, PyResult, types::PyList}; +# #[pyclass] #[derive(Clone)] struct MyClass { } +# Python::with_gil(|py| -> PyResult<()> { +let obj: &PyAny = Py::new(py, MyClass { })?.into_ref(py); + +// To &PyCell with PyAny::downcast +let _: &PyCell = obj.downcast()?; + +// To Py (aka PyObject) with .into() +let _: Py = obj.into(); + +// To Py with PyAny::extract +let _: Py = obj.extract()?; + +// To MyClass with PyAny::extract, if MyClass: Clone +let _: MyClass = obj.extract()?; + +// To PyRef or PyRefMut with PyAny::extract +let _: PyRef = obj.extract()?; +let _: PyRefMut = obj.extract()?; +# Ok(()) +# }).unwrap(); +``` ### `PyTuple`, `PyDict`, and many more @@ -99,29 +130,30 @@ To see all Python types exposed by `PyO3` you should consult the ```rust # use pyo3::prelude::*; # use pyo3::types::PyList; -# let gil = Python::acquire_gil(); -# let py = gil.python(); +# Python::with_gil(|py| -> PyResult<()> { let list = PyList::empty(py); -// Can use methods from PyAny on all Python types due to Deref implementation -let _ = list.repr(); +// Use methods from PyAny on all Python types with Deref implementation +let _ = list.repr()?; -// Rust will convert &PyList etc. to &PyAny automatically due to Deref implementation +// To &PyAny automatically with Deref implementation let _: &PyAny = list; -// For more explicit &PyAny conversion, use .as_ref() +// To &PyAny explicitly with .as_ref() let _: &PyAny = list.as_ref(); -// To convert to Py use .into() or Py::from() +// To Py with .into() or Py::from() let _: Py = list.into(); -// To convert to PyObject use .into() or .to_object(py) +// To PyObject with .into() or .to_object(py) let _: PyObject = list.into(); +# Ok(()) +# }).unwrap(); ``` -### `Py` +### `Py` and `PyObject` -**Represents:** a GIL independent reference to a Python object. This can be a Python native type +**Represents:** a GIL-independent reference to a Python object. This can be a Python native type (like `PyTuple`), or a `pyclass` type implemented in Rust. The most commonly-used variant, `Py`, is also known as `PyObject`. @@ -133,6 +165,8 @@ Can be cloned using Python reference counts with `.clone()`. **Conversions:** +For a `Py`, the conversions are as below: + ```rust # use pyo3::prelude::*; # use pyo3::types::PyList; @@ -140,20 +174,47 @@ Can be cloned using Python reference counts with `.clone()`. # let py = gil.python(); let list: Py = PyList::empty(py).into(); -// Access to the native type using Py::as_ref(py) or Py::into_ref(py) -// (For #[pyclass] types T, these will return &PyCell) - -// Py::as_ref() borrows the object +// To &PyList with Py::as_ref() (borrows from the Py) let _: &PyList = list.as_ref(py); -# let list_clone = list.clone(); // Just so that the .into() example for PyObject compiles. -// Py::into_ref() moves the reference into pyo3's "object storage"; useful for making APIs -// which return gil-bound references. +# let list_clone = list.clone(); // Because `.into_ref()` will consume `list`. +// To &PyList with Py::into_ref() (moves the pointer into PyO3's object storage) let _: &PyList = list.into_ref(py); # let list = list_clone; -// Convert to PyObject with .into() -let _: PyObject = list.into(); +// To Py (aka PyObject) with .into() +let _: Py = list.into(); +``` + +For a `#[pyclass] struct MyClass`, the conversions for `Py` are below: + +```rust +# use pyo3::prelude::*; +# let gil = Python::acquire_gil(); +# let py = gil.python(); +# #[pyclass] struct MyClass { } +# Python::with_gil(|py| -> PyResult<()> { +let my_class: Py = Py::new(py, MyClass { })?; + +// To &PyCell with Py::as_ref() (borrows from the Py) +let _: &PyCell = my_class.as_ref(py); + +# let my_class_clone = my_class.clone(); // Because `.into_ref()` will consume `my_class`. +// To &PyCell with Py::into_ref() (moves the pointer into PyO3's object storage) +let _: &PyCell = my_class.into_ref(py); + +# let my_class = my_class_clone.clone(); +// To Py (aka PyObject) with .into_py(py) +let _: Py = my_class.into_py(py); + +# let my_class = my_class_clone; +// To PyRef with Py::borrow or Py::try_borrow +let _: PyRef = my_class.try_borrow(py)?; + +// To PyRefMut with Py::borrow_mut or Py::try_borrow_mut +let _: PyRefMut = my_class.try_borrow_mut(py)?; +# Ok(()) +# }).unwrap(); ``` ### `PyCell` @@ -171,33 +232,46 @@ so it also exposes all of the methods on `PyAny`. **Conversions:** +`PyCell` can be used to access `&T` and `&mut T` via `PyRef` and `PyRefMut` respectively. + ```rust # use pyo3::prelude::*; # use pyo3::types::PyList; # #[pyclass] struct MyClass { } -# let gil = Python::acquire_gil(); -# let py = gil.python(); -let cell: &PyCell = PyCell::new(py, MyClass { }).unwrap(); +# Python::with_gil(|py| -> PyResult<()> { +let cell: &PyCell = PyCell::new(py, MyClass { })?; + +// To PyRef with .borrow() or .try_borrow() +let py_ref: PyRef = cell.try_borrow()?; +let _: &MyClass = &*py_ref; +# drop(py_ref); + +// To PyRefMut with .borrow_mut() or .try_borrow_mut() +let mut py_ref_mut: PyRefMut = cell.try_borrow_mut()?; +let _: &mut MyClass = &mut *py_ref_mut; +# Ok(()) +# }).unwrap(); +``` -// Obtain PyRef with .try_borrow() -let pr: PyRef = cell.try_borrow().unwrap(); -# drop(pr); +`PyCell` can also be accessed like a Python-native type. -// Obtain PyRefMut with .try_borrow_mut() -let prm: PyRefMut = cell.try_borrow_mut().unwrap(); -# drop(prm); +```rust +# use pyo3::prelude::*; +# use pyo3::types::PyList; +# #[pyclass] struct MyClass { } +# Python::with_gil(|py| -> PyResult<()> { +let cell: &PyCell = PyCell::new(py, MyClass { })?; -// Can use methods from PyAny on PyCell due to Deref implementation -let _ = cell.repr(); +// Use methods from PyAny on PyCell with Deref implementation +let _ = cell.repr()?; -// Rust will convert &PyCell to &PyAny automatically due to Deref implementation +// To &PyAny automatically with Deref implementation let _: &PyAny = cell; -// For more explicit &PyAny conversion, use .as_ref() -let any: &PyAny = cell.as_ref(); - -// To obtain a PyCell from PyAny, use PyAny::downcast -let _: &PyCell = any.downcast().unwrap(); +// To &PyAny explicitly with .as_ref() +let _: &PyAny = cell.as_ref(); +# Ok(()) +# }).unwrap(); ``` ### `PyRef` and `PyRefMut`