Python Iterator for Generic Rust Struct #2667
-
Hello! Me again. You probably know the story by now, I have a rust struct wrapping a generic vector for use in a python pipeline. The idea is to speed up array handling in Python. It looks like this (big thanks to contributors here for getting me this far): #[pyclass]
pub struct Wrapper {
inner: WrapperInner,
}
#[pymethods]
impl Wrapper {
#[new]
fn new(members: &PyAny) -> PyResult<Self> {
let inner = if let Ok(members) = Vec::<i64>::extract(members) {
WrapperInner::Int(Core { members })
} else if let Ok(members) = Vec::<String>::extract(members) {
WrapperInner::Str(Core { members })
// etc...
} Then, I have a core.rs containing the Rust Vector processing side: pub struct Core<T> {
pub members: Vec<T>,
}
impl<T> Core<T>
where // Traits I need
{
// fns
} Now, I'm trying to implement iteration on this struct (e.g. impl Core {
// fns ...
fn __iter__(slf: PyRef<Self>) -> PyResult<Py<Iter>> {
let iter = Iter {
inner: slf.members.clone().into_iter(),
};
Py::new(slf.py(), iter)
}
}
#[pyclass]
struct Iter {
inner: std::vec::IntoIter<String>,
}
#[pymethods]
impl Iter {
fn __iter__(slf: PyRef<Self>) -> PyRef<Self> {
slf
}
fn __next__(mut slf: PyRefMut<Self>) -> Option<String> {
slf.inner.next()
}
} Which was all fine and good - I was always sure of getting a String iterator out which made things easy. I tried to adapt this as follows, starting with ONLY int or string cases (I can extrapolate from there): // in wrapper.rs - I don't want to pollute core.rs with pyo3 context
impl Wrapper {
// fns...
fn iter(slf: PyRef<Self>) -> Result<Py<StrOrIntIter>, PyErr> {
let iter: StrOrIntIter = match &slf.inner {
WrapperInner::Str(core) => {
StrOrIntIter::Str(IterStr {
inner: core.members.clone().into_iter(),
})
},
WrapperInner::Int(core) => {
StrOrIntIter::Int(IterInt {
inner: core.members.clone().into_iter(),
})
},
};
Py::new(slf.py(), iter)
}
#[pyclass]
enum StrOrIntIter {
Str(IterStr),
Int(IterInt),
}
[pyclass]
pub struct IterInt {
inner: std::vec::IntoIter<i64>,
}
#[pyclass]
pub struct IterStr {
inner: std::vec::IntoIter<String>,
}
#[pymethods]
impl IterInt {
fn __iter__(slf: PyRef<Self>) -> PyRef<Self> {
slf
}
fn __next__(mut slf: PyRefMut<Self>) -> Option<i64> {
slf.inner.next()
}
}
#[pymethods]
impl IterStr {
fn __iter__(slf: PyRef<Self>) -> PyRef<Self> {
slf
}
fn __next__(mut slf: PyRefMut<Self>) -> Option<String> {
slf.inner.next()
}
} Of course, this won't work, because Pyo3 has no idea how to turn a StrOrIntIter into a python iterator (though it would understand how to return a My error message looks something like:
Now, I know exactly what this is telling me. I have to implement impl Into<PyClassInitializer<T>> for StrOrIntIter {
fn into(self, py: Python<'_>) => PyObject {
match self {
StrOrIntIter::Str(iter) -> Py::new(PyRef::new(py, self).py(), iter),
StrOrIntIter::Int(iter) -> Py::new(PyRef::new(py, self).py(), iter),
}
}
} But this has got me so many errors I don't know where to start. I can't find the documentation on this - any guidance would be appreciated! |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 3 replies
-
Implemeting However, if // in wrapper.rs - I don't want to pollute core.rs with pyo3 context
impl Wrapper {
// fns...
fn iter(&self, py: Python<'_>) -> Result<Py<PyAny>, PyErr> {
match &self.inner {
WrapperInner::Str(core) => {
IterStr {
inner: core.members.clone().into_iter(),
}.into_py(py)
},
WrapperInner::Int(core) => {
IterInt {
inner: core.members.clone().into_iter(),
}.into_py(py)
},
}
} Cloning the inner vector is not very efficient, by the way. Depending on the circumstances, it would probably be better to implement the |
Beta Was this translation helpful? Give feedback.
-
You can't use |
Beta Was this translation helpful? Give feedback.
Implemeting
Into<PyClassInitializer>
is not going to help here. You could implementPyClass
forStrOrIntIter
, forwarding the__iter__
and__next__
methods to the inner types.However, if
StrOrIntIter
is only needed for the benefit of Python code, I think it would be easier to have theiter
returnPy<PyAny>
, without a wrapper type, somewhat like this: