Skip to content

Commit 746c352

Browse files
author
Austin Bingham
authored
Clarified documentation for implementing iteration. (#882)
* Clarified documentation for implementing iteration. * Updated example so that it compiles. * Updated PyIterProtocol docs per discussion. * Added link to Python docs on iterators. * Improved language regarding iterables and PyIterProtocol. * Fixed assertion calls.
1 parent e9e0da5 commit 746c352

File tree

1 file changed

+56
-0
lines changed

1 file changed

+56
-0
lines changed

guide/src/class.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,62 @@ impl PyIterProtocol for MyIterator {
835835
}
836836
```
837837

838+
In many cases you'll have a distinction between the type being iterated over (i.e. the *iterable*) and the iterator it
839+
provides. In this case, you should implement `PyIterProtocol` for both the iterable and the iterator, but the iterable
840+
only needs to support `__iter__()` while the iterator must support both `__iter__()` and `__next__()`. The default
841+
implementations in `PyIterProtocol` will ensure that the objects behave correctly in Python. For example:
842+
843+
```rust
844+
# use pyo3::prelude::*;
845+
# use pyo3::PyIterProtocol;
846+
847+
#[pyclass]
848+
struct Iter {
849+
inner: std::vec::IntoIter<usize>,
850+
}
851+
852+
#[pyproto]
853+
impl PyIterProtocol for Iter {
854+
fn __iter__(slf: PyRefMut<Self>) -> PyResult<Py<Iter>> {
855+
Ok(slf.into())
856+
}
857+
858+
fn __next__(mut slf: PyRefMut<Self>) -> PyResult<Option<usize>> {
859+
Ok(slf.inner.next())
860+
}
861+
}
862+
863+
#[pyclass]
864+
struct Container {
865+
iter: Vec<usize>,
866+
}
867+
868+
#[pyproto]
869+
impl PyIterProtocol for Container {
870+
fn __iter__(slf: PyRefMut<Self>) -> PyResult<Py<Iter>> {
871+
let iter = Iter {
872+
inner: slf.iter.clone().into_iter(),
873+
};
874+
PyCell::new(slf.py(), iter).map(Into::into)
875+
}
876+
}
877+
878+
# let gil = Python::acquire_gil();
879+
# let py = gil.python();
880+
# let inst = pyo3::PyCell::new(
881+
# py,
882+
# Container {
883+
# iter: vec![1, 2, 3, 4],
884+
# },
885+
# )
886+
# .unwrap();
887+
# pyo3::py_run!(py, inst, "assert list(inst) == [1, 2, 3, 4]");
888+
# pyo3::py_run!(py, inst, "assert list(iter(iter(inst))) == [1, 2, 3, 4]");
889+
```
890+
891+
For more details on Python's iteration protocols, check out [the "Iterator Types" section of the library
892+
documentation](https://docs.python.org/3/library/stdtypes.html#iterator-types).
893+
838894
## How methods are implemented
839895

840896
Users should be able to define a `#[pyclass]` with or without `#[pymethods]`, while PyO3 needs a

0 commit comments

Comments
 (0)