Skip to content

Commit 71100db

Browse files
ngoldbaumIcxolu
andauthored
docs: add narrative docs for BoundObject (#4703)
* docs: add narrative docs for BoundObject * grammar fixes * use where to break up signature * fix incorrect last sentence * Update guide/src/types.md Co-authored-by: Icxolu <[email protected]> * move example to traits.md and rework with a stronger motivation * fix cross-reference target * fix type * build result vec without copying * simplify example a little * update explanation sentence * fix API docs link --------- Co-authored-by: Icxolu <[email protected]>
1 parent ee229cf commit 71100db

File tree

2 files changed

+70
-0
lines changed

2 files changed

+70
-0
lines changed

guide/src/conversions/traits.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,73 @@ impl IntoPy<PyObject> for MyPyObjectWrapper {
626626
}
627627
```
628628

629+
#### `BoundObject` for conversions that may be `Bound` or `Borrowed`
630+
631+
`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>`:
632+
633+
```rust
634+
use pyo3::prelude::*;
635+
use pyo3::IntoPyObject;
636+
use pyo3::types::{PyBool, PyInt};
637+
638+
let ints: Vec<u32> = vec![1, 2, 3, 4];
639+
let bools = vec![true, false, false, true];
640+
641+
Python::with_gil(|py| {
642+
let ints_as_pyint: Vec<Bound<'_, PyInt>> = ints
643+
.iter()
644+
.map(|x| Ok(x.into_pyobject(py)?))
645+
.collect::<PyResult<_>>()
646+
.unwrap();
647+
648+
let bools_as_pybool: Vec<Borrowed<'_, '_, PyBool>> = bools
649+
.iter()
650+
.map(|x| Ok(x.into_pyobject(py)?))
651+
.collect::<PyResult<_>>()
652+
.unwrap();
653+
});
654+
```
655+
656+
In this example if we wanted to combine `ints_as_pyints` and `bools_as_pybool` into a single `Vec<Py<PyAny>>` to return from the `with_gil` closure, we would have to manually convert the concrete types for the smart pointers and the python types.
657+
658+
Instead, we can write a function that generically converts vectors of either integers or bools into a vector of `Py<PyAny>` using the [`BoundObject`] trait:
659+
660+
```rust
661+
# use pyo3::prelude::*;
662+
# use pyo3::BoundObject;
663+
# use pyo3::IntoPyObject;
664+
665+
# let bools = vec![true, false, false, true];
666+
# let ints = vec![1, 2, 3, 4];
667+
668+
fn convert_to_vec_of_pyobj<'py, T>(py: Python<'py>, the_vec: Vec<T>) -> PyResult<Vec<Py<PyAny>>>
669+
where
670+
T: IntoPyObject<'py> + Copy
671+
{
672+
the_vec.iter()
673+
.map(|x| {
674+
Ok(
675+
x.into_pyobject(py)
676+
.map_err(Into::into)?
677+
.into_any()
678+
.unbind()
679+
)
680+
})
681+
.collect()
682+
}
683+
684+
let vec_of_pyobjs: Vec<Py<PyAny>> = Python::with_gil(|py| {
685+
let mut bools_as_pyany = convert_to_vec_of_pyobj(py, bools).unwrap();
686+
let mut ints_as_pyany = convert_to_vec_of_pyobj(py, ints).unwrap();
687+
let mut result: Vec<Py<PyAny>> = vec![];
688+
result.append(&mut bools_as_pyany);
689+
result.append(&mut ints_as_pyany);
690+
result
691+
});
692+
```
693+
694+
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.
695+
629696
### The `ToPyObject` trait
630697

631698
<div class="warning">
@@ -647,3 +714,4 @@ same purpose, except that it consumes `self`.
647714

648715
[`PyRef`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyRef.html
649716
[`PyRefMut`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyRefMut.html
717+
[`BoundObject`]: {{#PYO3_DOCS_URL}}/pyo3/instance/trait.BoundObject.html

guide/src/migration.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@ need to adapt an implementation of `IntoPyObject` to stay compatible with the Py
157157
the new [`#[derive(IntoPyObject)]`](#intopyobject-derive-macro) macro can be used instead of
158158
[manual implementations](#intopyobject-manual-implementation).
159159

160+
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.
161+
160162
Together with the introduction of `IntoPyObject` the old conversion traits `ToPyObject` and `IntoPy`
161163
are deprecated and will be removed in a future PyO3 version.
162164

0 commit comments

Comments
 (0)