diff --git a/guide/src/conversions/traits.md b/guide/src/conversions/traits.md index a0e6ec6db0e..5e3da6ea1b0 100644 --- a/guide/src/conversions/traits.md +++ b/guide/src/conversions/traits.md @@ -626,6 +626,73 @@ impl IntoPy for MyPyObjectWrapper { } ``` +#### `BoundObject` for conversions that may be `Bound` or `Borrowed` + +`IntoPyObject::into_py_object` returns either `Bound` or `Borrowed` depending on the implementation for a concrete type. For example, the `IntoPyObject` implementation for `u32` produces a `Bound<'py, PyInt>` and the `bool` implementation produces a `Borrowed<'py, 'py, PyBool>`: + +```rust +use pyo3::prelude::*; +use pyo3::IntoPyObject; +use pyo3::types::{PyBool, PyInt}; + +let ints: Vec = vec![1, 2, 3, 4]; +let bools = vec![true, false, false, true]; + +Python::with_gil(|py| { + let ints_as_pyint: Vec> = ints + .iter() + .map(|x| Ok(x.into_pyobject(py)?)) + .collect::>() + .unwrap(); + + let bools_as_pybool: Vec> = bools + .iter() + .map(|x| Ok(x.into_pyobject(py)?)) + .collect::>() + .unwrap(); +}); +``` + +In this example if we wanted to combine `ints_as_pyints` and `bools_as_pybool` into a single `Vec>` to return from the `with_gil` closure, we would have to manually convert the concrete types for the smart pointers and the python types. + +Instead, we can write a function that generically converts vectors of either integers or bools into a vector of `Py` using the [`BoundObject`] trait: + +```rust +# use pyo3::prelude::*; +# use pyo3::BoundObject; +# use pyo3::IntoPyObject; + +# let bools = vec![true, false, false, true]; +# let ints = vec![1, 2, 3, 4]; + +fn convert_to_vec_of_pyobj<'py, T>(py: Python<'py>, the_vec: Vec) -> PyResult>> +where + T: IntoPyObject<'py> + Copy +{ + the_vec.iter() + .map(|x| { + Ok( + x.into_pyobject(py) + .map_err(Into::into)? + .into_any() + .unbind() + ) + }) + .collect() +} + +let vec_of_pyobjs: Vec> = Python::with_gil(|py| { + let mut bools_as_pyany = convert_to_vec_of_pyobj(py, bools).unwrap(); + let mut ints_as_pyany = convert_to_vec_of_pyobj(py, ints).unwrap(); + let mut result: Vec> = vec![]; + result.append(&mut bools_as_pyany); + result.append(&mut ints_as_pyany); + result +}); +``` + +In the example above we used `BoundObject::into_any` and `BoundObject::unbind` to manipulate the python types and smart pointers into the result type we wanted to produce from the function. + ### The `ToPyObject` trait
@@ -647,3 +714,4 @@ same purpose, except that it consumes `self`. [`PyRef`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyRef.html [`PyRefMut`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyRefMut.html +[`BoundObject`]: {{#PYO3_DOCS_URL}}/pyo3/instance/trait.BoundObject.html diff --git a/guide/src/migration.md b/guide/src/migration.md index 1b8400604cb..3ad09f7eef0 100644 --- a/guide/src/migration.md +++ b/guide/src/migration.md @@ -157,6 +157,8 @@ need to adapt an implementation of `IntoPyObject` to stay compatible with the Py the new [`#[derive(IntoPyObject)]`](#intopyobject-derive-macro) macro can be used instead of [manual implementations](#intopyobject-manual-implementation). +Since `IntoPyObject::into_pyobject` may return either a `Bound` or `Borrowed`, you may find the [`BoundObject`](conversions/traits.md#boundobject-for-conversions-that-may-be-bound-or-borrowed) trait to be useful to write code that generically handles either type of smart pointer. + Together with the introduction of `IntoPyObject` the old conversion traits `ToPyObject` and `IntoPy` are deprecated and will be removed in a future PyO3 version.