diff --git a/src/instance.rs b/src/instance.rs index 14e6000caee..e8ff4a2f333 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -221,6 +221,8 @@ unsafe impl AsPyPointer for Py2<'_, T> { /// /// The advantage of this over `&Py2` is that it avoids the need to have a pointer-to-pointer, as Py2 /// is already a pointer to an `ffi::PyObject``. +/// +/// Similarly, this type is `Copy` and `Clone`, like a shared reference (`&T`). #[repr(transparent)] pub(crate) struct Py2Borrowed<'a, 'py, T>( NonNull, @@ -228,6 +230,17 @@ pub(crate) struct Py2Borrowed<'a, 'py, T>( Python<'py>, ); +impl<'py, T> Py2Borrowed<'_, 'py, T> { + /// Creates a new owned `Py2` from this borrowed reference by increasing the reference count. + pub(crate) fn to_owned(self) -> Py2<'py, T> { + unsafe { ffi::Py_INCREF(self.as_ptr()) }; + Py2( + self.py(), + ManuallyDrop::new(unsafe { Py::from_non_null(self.0) }), + ) + } +} + impl<'a, 'py> Py2Borrowed<'a, 'py, PyAny> { /// # Safety /// This is similar to `std::slice::from_raw_parts`, the lifetime `'a` is completely defined by @@ -315,6 +328,14 @@ impl<'py, T> Deref for Py2Borrowed<'_, 'py, T> { } } +impl Clone for Py2Borrowed<'_, '_, T> { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for Py2Borrowed<'_, '_, T> {} + /// A GIL-independent reference to an object allocated on the Python heap. /// /// This type does not auto-dereference to the inner object because you must prove you hold the GIL to access it. diff --git a/src/prelude.rs b/src/prelude.rs index 301e62b3639..54860bf9daf 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -32,6 +32,7 @@ pub use crate::wrap_pyfunction; // pub(crate) use crate::types::bytes::PyBytesMethods; // pub(crate) use crate::types::dict::PyDictMethods; // pub(crate) use crate::types::float::PyFloatMethods; +// pub(crate) use crate::types::list::PyListMethods; // pub(crate) use crate::types::mapping::PyMappingMethods; // pub(crate) use crate::types::sequence::PySequenceMethods; // pub(crate) use crate::types::string::PyStringMethods; diff --git a/src/types/dict.rs b/src/types/dict.rs index c03a03ec534..858fb1872ed 100644 --- a/src/types/dict.rs +++ b/src/types/dict.rs @@ -2,7 +2,7 @@ use super::PyMapping; use crate::err::{self, PyErr, PyResult}; use crate::ffi::Py_ssize_t; use crate::ffi_ptr_ext::FfiPtrExt; -use crate::instance::Py2; +use crate::instance::{Py2, Py2Borrowed}; use crate::py_result_ext::PyResultExt; use crate::types::any::PyAnyMethods; use crate::types::{PyAny, PyList}; @@ -406,7 +406,7 @@ impl<'py> PyDictMethods<'py> for Py2<'py, PyDict> { match unsafe { ffi::PyDict_GetItemWithError(dict.as_ptr(), key.as_ptr()) .assume_borrowed_or_opt(py) - .map(|borrowed_any| borrowed_any.clone()) + .map(Py2Borrowed::to_owned) } { some @ Some(_) => Ok(some), None => PyErr::take(py).map(Err).transpose(), @@ -595,8 +595,8 @@ impl<'py> Iterator for PyDictIterator2<'py> { // - PyDict_Next returns borrowed values // - we have already checked that `PyDict_Next` succeeded, so we can assume these to be non-null Some(( - unsafe { key.assume_borrowed_unchecked(py) }.clone(), - unsafe { value.assume_borrowed_unchecked(py) }.clone(), + unsafe { key.assume_borrowed_unchecked(py) }.to_owned(), + unsafe { value.assume_borrowed_unchecked(py) }.to_owned(), )) } else { None diff --git a/src/types/list.rs b/src/types/list.rs index 71261ba6baa..661034111fb 100644 --- a/src/types/list.rs +++ b/src/types/list.rs @@ -3,9 +3,14 @@ use std::iter::FusedIterator; use crate::err::{self, PyResult}; use crate::ffi::{self, Py_ssize_t}; +use crate::ffi_ptr_ext::FfiPtrExt; +use crate::instance::Py2Borrowed; use crate::internal_tricks::get_ssize_index; use crate::types::{PySequence, PyTuple}; -use crate::{Py, PyAny, PyObject, Python, ToPyObject}; +use crate::{Py2, PyAny, PyObject, Python, ToPyObject}; + +use crate::types::any::PyAnyMethods; +use crate::types::sequence::PySequenceMethods; /// Represents a Python `list`. #[repr(transparent)] @@ -15,10 +20,10 @@ pyobject_native_type_core!(PyList, pyobject_native_static_type_object!(ffi::PyLi #[inline] #[track_caller] -pub(crate) fn new_from_iter( - py: Python<'_>, +pub(crate) fn new_from_iter<'py>( + py: Python<'py>, elements: &mut dyn ExactSizeIterator, -) -> Py { +) -> Py2<'py, PyList> { unsafe { // PyList_New checks for overflow but has a bad error message, so we check ourselves let len: Py_ssize_t = elements @@ -28,10 +33,10 @@ pub(crate) fn new_from_iter( let ptr = ffi::PyList_New(len); - // We create the `Py` pointer here for two reasons: + // We create the `Py2` pointer here for two reasons: // - panics if the ptr is null // - its Drop cleans up the list if user code or the asserts panic. - let list: Py = Py::from_owned_ptr(py, ptr); + let list = ptr.assume_owned(py).downcast_into_unchecked(); let mut counter: Py_ssize_t = 0; @@ -83,17 +88,274 @@ impl PyList { U: ExactSizeIterator, { let mut iter = elements.into_iter().map(|e| e.to_object(py)); - let list = new_from_iter(py, &mut iter); - list.into_ref(py) + new_from_iter(py, &mut iter).into_gil_ref() } /// Constructs a new empty list. pub fn empty(py: Python<'_>) -> &PyList { - unsafe { py.from_owned_ptr::(ffi::PyList_New(0)) } + unsafe { py.from_owned_ptr(ffi::PyList_New(0)) } } /// Returns the length of the list. pub fn len(&self) -> usize { + Py2::borrowed_from_gil_ref(&self).len() + } + + /// Checks if the list is empty. + pub fn is_empty(&self) -> bool { + Py2::borrowed_from_gil_ref(&self).is_empty() + } + + /// Returns `self` cast as a `PySequence`. + pub fn as_sequence(&self) -> &PySequence { + unsafe { self.downcast_unchecked() } + } + + /// Gets the list item at the specified index. + /// # Example + /// ``` + /// use pyo3::{prelude::*, types::PyList}; + /// Python::with_gil(|py| { + /// let list = PyList::new(py, [2, 3, 5, 7]); + /// let obj = list.get_item(0); + /// assert_eq!(obj.unwrap().extract::().unwrap(), 2); + /// }); + /// ``` + pub fn get_item(&self, index: usize) -> PyResult<&PyAny> { + Py2::borrowed_from_gil_ref(&self) + .get_item(index) + .map(Py2::into_gil_ref) + } + + /// Gets the list item at the specified index. Undefined behavior on bad index. Use with caution. + /// + /// # Safety + /// + /// Caller must verify that the index is within the bounds of the list. + #[cfg(not(Py_LIMITED_API))] + pub unsafe fn get_item_unchecked(&self, index: usize) -> &PyAny { + Py2::borrowed_from_gil_ref(&self) + .get_item_unchecked(index) + .into_gil_ref() + } + + /// Takes the slice `self[low:high]` and returns it as a new list. + /// + /// Indices must be nonnegative, and out-of-range indices are clipped to + /// `self.len()`. + pub fn get_slice(&self, low: usize, high: usize) -> &PyList { + Py2::borrowed_from_gil_ref(&self) + .get_slice(low, high) + .into_gil_ref() + } + + /// Sets the item at the specified index. + /// + /// Raises `IndexError` if the index is out of range. + pub fn set_item(&self, index: usize, item: I) -> PyResult<()> + where + I: ToPyObject, + { + Py2::borrowed_from_gil_ref(&self).set_item(index, item) + } + + /// Deletes the `index`th element of self. + /// + /// This is equivalent to the Python statement `del self[i]`. + #[inline] + pub fn del_item(&self, index: usize) -> PyResult<()> { + Py2::borrowed_from_gil_ref(&self).del_item(index) + } + + /// Assigns the sequence `seq` to the slice of `self` from `low` to `high`. + /// + /// This is equivalent to the Python statement `self[low:high] = v`. + #[inline] + pub fn set_slice(&self, low: usize, high: usize, seq: &PyAny) -> PyResult<()> { + Py2::borrowed_from_gil_ref(&self).set_slice(low, high, Py2::borrowed_from_gil_ref(&seq)) + } + + /// Deletes the slice from `low` to `high` from `self`. + /// + /// This is equivalent to the Python statement `del self[low:high]`. + #[inline] + pub fn del_slice(&self, low: usize, high: usize) -> PyResult<()> { + Py2::borrowed_from_gil_ref(&self).del_slice(low, high) + } + + /// Appends an item to the list. + pub fn append(&self, item: I) -> PyResult<()> + where + I: ToPyObject, + { + Py2::borrowed_from_gil_ref(&self).append(item) + } + + /// Inserts an item at the specified index. + /// + /// If `index >= self.len()`, inserts at the end. + pub fn insert(&self, index: usize, item: I) -> PyResult<()> + where + I: ToPyObject, + { + Py2::borrowed_from_gil_ref(&self).insert(index, item) + } + + /// Determines if self contains `value`. + /// + /// This is equivalent to the Python expression `value in self`. + #[inline] + pub fn contains(&self, value: V) -> PyResult + where + V: ToPyObject, + { + Py2::borrowed_from_gil_ref(&self).contains(value) + } + + /// Returns the first index `i` for which `self[i] == value`. + /// + /// This is equivalent to the Python expression `self.index(value)`. + #[inline] + pub fn index(&self, value: V) -> PyResult + where + V: ToPyObject, + { + Py2::borrowed_from_gil_ref(&self).index(value) + } + + /// Returns an iterator over this list's items. + pub fn iter(&self) -> PyListIterator<'_> { + PyListIterator(Py2::borrowed_from_gil_ref(&self).iter()) + } + + /// Sorts the list in-place. Equivalent to the Python expression `l.sort()`. + pub fn sort(&self) -> PyResult<()> { + Py2::borrowed_from_gil_ref(&self).sort() + } + + /// Reverses the list in-place. Equivalent to the Python expression `l.reverse()`. + pub fn reverse(&self) -> PyResult<()> { + Py2::borrowed_from_gil_ref(&self).reverse() + } + + /// Return a new tuple containing the contents of the list; equivalent to the Python expression `tuple(list)`. + /// + /// This method is equivalent to `self.as_sequence().to_tuple()` and faster than `PyTuple::new(py, this_list)`. + pub fn to_tuple(&self) -> &PyTuple { + Py2::borrowed_from_gil_ref(&self).to_tuple().into_gil_ref() + } +} + +index_impls!(PyList, "list", PyList::len, PyList::get_slice); + +/// Implementation of functionality for [`PyList`]. +/// +/// These methods are defined for the `Py2<'py, PyList>` smart pointer, so to use method call +/// syntax these methods are separated into a trait, because stable Rust does not yet support +/// `arbitrary_self_types`. +#[doc(alias = "PyList")] +pub(crate) trait PyListMethods<'py> { + /// Returns the length of the list. + fn len(&self) -> usize; + + /// Checks if the list is empty. + fn is_empty(&self) -> bool; + + /// Returns `self` cast as a `PySequence`. + fn as_sequence(&self) -> &Py2<'py, PySequence>; + + /// Gets the list item at the specified index. + /// # Example + /// ``` + /// use pyo3::{prelude::*, types::PyList}; + /// Python::with_gil(|py| { + /// let list = PyList::new(py, [2, 3, 5, 7]); + /// let obj = list.get_item(0); + /// assert_eq!(obj.unwrap().extract::().unwrap(), 2); + /// }); + /// ``` + fn get_item(&self, index: usize) -> PyResult>; + + /// Gets the list item at the specified index. Undefined behavior on bad index. Use with caution. + /// + /// # Safety + /// + /// Caller must verify that the index is within the bounds of the list. + #[cfg(not(Py_LIMITED_API))] + unsafe fn get_item_unchecked(&self, index: usize) -> Py2<'py, PyAny>; + + /// Takes the slice `self[low:high]` and returns it as a new list. + /// + /// Indices must be nonnegative, and out-of-range indices are clipped to + /// `self.len()`. + fn get_slice(&self, low: usize, high: usize) -> Py2<'py, PyList>; + + /// Sets the item at the specified index. + /// + /// Raises `IndexError` if the index is out of range. + fn set_item(&self, index: usize, item: I) -> PyResult<()> + where + I: ToPyObject; + + /// Deletes the `index`th element of self. + /// + /// This is equivalent to the Python statement `del self[i]`. + fn del_item(&self, index: usize) -> PyResult<()>; + + /// Assigns the sequence `seq` to the slice of `self` from `low` to `high`. + /// + /// This is equivalent to the Python statement `self[low:high] = v`. + fn set_slice(&self, low: usize, high: usize, seq: &Py2<'_, PyAny>) -> PyResult<()>; + + /// Deletes the slice from `low` to `high` from `self`. + /// + /// This is equivalent to the Python statement `del self[low:high]`. + fn del_slice(&self, low: usize, high: usize) -> PyResult<()>; + + /// Appends an item to the list. + fn append(&self, item: I) -> PyResult<()> + where + I: ToPyObject; + + /// Inserts an item at the specified index. + /// + /// If `index >= self.len()`, inserts at the end. + fn insert(&self, index: usize, item: I) -> PyResult<()> + where + I: ToPyObject; + + /// Determines if self contains `value`. + /// + /// This is equivalent to the Python expression `value in self`. + fn contains(&self, value: V) -> PyResult + where + V: ToPyObject; + + /// Returns the first index `i` for which `self[i] == value`. + /// + /// This is equivalent to the Python expression `self.index(value)`. + fn index(&self, value: V) -> PyResult + where + V: ToPyObject; + + /// Returns an iterator over this list's items. + fn iter(&self) -> PyListIterator2<'py>; + + /// Sorts the list in-place. Equivalent to the Python expression `l.sort()`. + fn sort(&self) -> PyResult<()>; + + /// Reverses the list in-place. Equivalent to the Python expression `l.reverse()`. + fn reverse(&self) -> PyResult<()>; + + /// Return a new tuple containing the contents of the list; equivalent to the Python expression `tuple(list)`. + /// + /// This method is equivalent to `self.as_sequence().to_tuple()` and faster than `PyTuple::new(py, this_list)`. + fn to_tuple(&self) -> Py2<'py, PyTuple>; +} + +impl<'py> PyListMethods<'py> for Py2<'py, PyList> { + /// Returns the length of the list. + fn len(&self) -> usize { unsafe { #[cfg(not(Py_LIMITED_API))] let size = ffi::PyList_GET_SIZE(self.as_ptr()); @@ -106,12 +368,12 @@ impl PyList { } /// Checks if the list is empty. - pub fn is_empty(&self) -> bool { + fn is_empty(&self) -> bool { self.len() == 0 } /// Returns `self` cast as a `PySequence`. - pub fn as_sequence(&self) -> &PySequence { + fn as_sequence(&self) -> &Py2<'py, PySequence> { unsafe { self.downcast_unchecked() } } @@ -125,12 +387,12 @@ impl PyList { /// assert_eq!(obj.unwrap().extract::().unwrap(), 2); /// }); /// ``` - pub fn get_item(&self, index: usize) -> PyResult<&PyAny> { + fn get_item(&self, index: usize) -> PyResult> { unsafe { - let item = ffi::PyList_GetItem(self.as_ptr(), index as Py_ssize_t); // PyList_GetItem return borrowed ptr; must make owned for safety (see #890). - ffi::Py_XINCREF(item); - self.py().from_owned_ptr_or_err(item) + ffi::PyList_GetItem(self.as_ptr(), index as Py_ssize_t) + .assume_borrowed_or_err(self.py()) + .map(Py2Borrowed::to_owned) } } @@ -140,48 +402,47 @@ impl PyList { /// /// Caller must verify that the index is within the bounds of the list. #[cfg(not(Py_LIMITED_API))] - pub unsafe fn get_item_unchecked(&self, index: usize) -> &PyAny { - let item = ffi::PyList_GET_ITEM(self.as_ptr(), index as Py_ssize_t); + unsafe fn get_item_unchecked(&self, index: usize) -> Py2<'py, PyAny> { // PyList_GET_ITEM return borrowed ptr; must make owned for safety (see #890). - ffi::Py_XINCREF(item); - self.py().from_owned_ptr(item) + ffi::PyList_GET_ITEM(self.as_ptr(), index as Py_ssize_t) + .assume_borrowed(self.py()) + .to_owned() } /// Takes the slice `self[low:high]` and returns it as a new list. /// /// Indices must be nonnegative, and out-of-range indices are clipped to /// `self.len()`. - pub fn get_slice(&self, low: usize, high: usize) -> &PyList { + fn get_slice(&self, low: usize, high: usize) -> Py2<'py, PyList> { unsafe { - self.py().from_owned_ptr(ffi::PyList_GetSlice( - self.as_ptr(), - get_ssize_index(low), - get_ssize_index(high), - )) + ffi::PyList_GetSlice(self.as_ptr(), get_ssize_index(low), get_ssize_index(high)) + .assume_owned(self.py()) + .downcast_into_unchecked() } } /// Sets the item at the specified index. /// /// Raises `IndexError` if the index is out of range. - pub fn set_item(&self, index: usize, item: I) -> PyResult<()> + fn set_item(&self, index: usize, item: I) -> PyResult<()> where I: ToPyObject, { - fn inner(list: &PyList, index: usize, item: PyObject) -> PyResult<()> { + fn inner(list: &Py2<'_, PyList>, index: usize, item: Py2<'_, PyAny>) -> PyResult<()> { err::error_on_minusone(list.py(), unsafe { ffi::PyList_SetItem(list.as_ptr(), get_ssize_index(index), item.into_ptr()) }) } - inner(self, index, item.to_object(self.py())) + let py = self.py(); + inner(self, index, item.to_object(py).attach_into(py)) } /// Deletes the `index`th element of self. /// /// This is equivalent to the Python statement `del self[i]`. #[inline] - pub fn del_item(&self, index: usize) -> PyResult<()> { + fn del_item(&self, index: usize) -> PyResult<()> { self.as_sequence().del_item(index) } @@ -189,7 +450,7 @@ impl PyList { /// /// This is equivalent to the Python statement `self[low:high] = v`. #[inline] - pub fn set_slice(&self, low: usize, high: usize, seq: &PyAny) -> PyResult<()> { + fn set_slice(&self, low: usize, high: usize, seq: &Py2<'_, PyAny>) -> PyResult<()> { err::error_on_minusone(self.py(), unsafe { ffi::PyList_SetSlice( self.as_ptr(), @@ -204,45 +465,47 @@ impl PyList { /// /// This is equivalent to the Python statement `del self[low:high]`. #[inline] - pub fn del_slice(&self, low: usize, high: usize) -> PyResult<()> { + fn del_slice(&self, low: usize, high: usize) -> PyResult<()> { self.as_sequence().del_slice(low, high) } /// Appends an item to the list. - pub fn append(&self, item: I) -> PyResult<()> + fn append(&self, item: I) -> PyResult<()> where I: ToPyObject, { - fn inner(list: &PyList, item: PyObject) -> PyResult<()> { + fn inner(list: &Py2<'_, PyList>, item: Py2<'_, PyAny>) -> PyResult<()> { err::error_on_minusone(list.py(), unsafe { ffi::PyList_Append(list.as_ptr(), item.as_ptr()) }) } - inner(self, item.to_object(self.py())) + let py = self.py(); + inner(self, item.to_object(py).attach_into(py)) } /// Inserts an item at the specified index. /// /// If `index >= self.len()`, inserts at the end. - pub fn insert(&self, index: usize, item: I) -> PyResult<()> + fn insert(&self, index: usize, item: I) -> PyResult<()> where I: ToPyObject, { - fn inner(list: &PyList, index: usize, item: PyObject) -> PyResult<()> { + fn inner(list: &Py2<'_, PyList>, index: usize, item: Py2<'_, PyAny>) -> PyResult<()> { err::error_on_minusone(list.py(), unsafe { ffi::PyList_Insert(list.as_ptr(), get_ssize_index(index), item.as_ptr()) }) } - inner(self, index, item.to_object(self.py())) + let py = self.py(); + inner(self, index, item.to_object(py).attach_into(py)) } /// Determines if self contains `value`. /// /// This is equivalent to the Python expression `value in self`. #[inline] - pub fn contains(&self, value: V) -> PyResult + fn contains(&self, value: V) -> PyResult where V: ToPyObject, { @@ -253,7 +516,7 @@ impl PyList { /// /// This is equivalent to the Python expression `self.index(value)`. #[inline] - pub fn index(&self, value: V) -> PyResult + fn index(&self, value: V) -> PyResult where V: ToPyObject, { @@ -261,43 +524,91 @@ impl PyList { } /// Returns an iterator over this list's items. - pub fn iter(&self) -> PyListIterator<'_> { - PyListIterator { - list: self, - index: 0, - length: self.len(), - } + fn iter(&self) -> PyListIterator2<'py> { + PyListIterator2::new(self.clone()) } /// Sorts the list in-place. Equivalent to the Python expression `l.sort()`. - pub fn sort(&self) -> PyResult<()> { + fn sort(&self) -> PyResult<()> { err::error_on_minusone(self.py(), unsafe { ffi::PyList_Sort(self.as_ptr()) }) } /// Reverses the list in-place. Equivalent to the Python expression `l.reverse()`. - pub fn reverse(&self) -> PyResult<()> { + fn reverse(&self) -> PyResult<()> { err::error_on_minusone(self.py(), unsafe { ffi::PyList_Reverse(self.as_ptr()) }) } /// Return a new tuple containing the contents of the list; equivalent to the Python expression `tuple(list)`. /// /// This method is equivalent to `self.as_sequence().to_tuple()` and faster than `PyTuple::new(py, this_list)`. - pub fn to_tuple(&self) -> &PyTuple { - unsafe { self.py().from_owned_ptr(ffi::PyList_AsTuple(self.as_ptr())) } + fn to_tuple(&self) -> Py2<'py, PyTuple> { + unsafe { + ffi::PyList_AsTuple(self.as_ptr()) + .assume_owned(self.py()) + .downcast_into_unchecked() + } } } -index_impls!(PyList, "list", PyList::len, PyList::get_slice); +/// Used by `PyList::iter()`. +pub struct PyListIterator<'a>(PyListIterator2<'a>); + +impl<'a> Iterator for PyListIterator<'a> { + type Item = &'a PyAny; + + #[inline] + fn next(&mut self) -> Option { + self.0.next().map(Py2::into_gil_ref) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +impl<'a> DoubleEndedIterator for PyListIterator<'a> { + #[inline] + fn next_back(&mut self) -> Option { + self.0.next_back().map(Py2::into_gil_ref) + } +} + +impl<'a> ExactSizeIterator for PyListIterator<'a> { + fn len(&self) -> usize { + self.0.len() + } +} + +impl FusedIterator for PyListIterator<'_> {} + +impl<'a> IntoIterator for &'a PyList { + type Item = &'a PyAny; + type IntoIter = PyListIterator<'a>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} /// Used by `PyList::iter()`. -pub struct PyListIterator<'a> { - list: &'a PyList, +pub(crate) struct PyListIterator2<'py> { + list: Py2<'py, PyList>, index: usize, length: usize, } -impl<'a> PyListIterator<'a> { - unsafe fn get_item(&self, index: usize) -> &'a PyAny { +impl<'py> PyListIterator2<'py> { + fn new(list: Py2<'py, PyList>) -> Self { + let length: usize = list.len(); + PyListIterator2 { + list, + index: 0, + length, + } + } + + unsafe fn get_item(&self, index: usize) -> Py2<'py, PyAny> { #[cfg(any(Py_LIMITED_API, PyPy))] let item = self.list.get_item(index).expect("list.get failed"); #[cfg(not(any(Py_LIMITED_API, PyPy)))] @@ -306,8 +617,8 @@ impl<'a> PyListIterator<'a> { } } -impl<'a> Iterator for PyListIterator<'a> { - type Item = &'a PyAny; +impl<'py> Iterator for PyListIterator2<'py> { + type Item = Py2<'py, PyAny>; #[inline] fn next(&mut self) -> Option { @@ -329,7 +640,7 @@ impl<'a> Iterator for PyListIterator<'a> { } } -impl<'a> DoubleEndedIterator for PyListIterator<'a> { +impl DoubleEndedIterator for PyListIterator2<'_> { #[inline] fn next_back(&mut self) -> Option { let length = self.length.min(self.list.len()); @@ -344,23 +655,32 @@ impl<'a> DoubleEndedIterator for PyListIterator<'a> { } } -impl<'a> ExactSizeIterator for PyListIterator<'a> { +impl ExactSizeIterator for PyListIterator2<'_> { fn len(&self) -> usize { self.length.saturating_sub(self.index) } } -impl FusedIterator for PyListIterator<'_> {} +impl FusedIterator for PyListIterator2<'_> {} -impl<'a> IntoIterator for &'a PyList { - type Item = &'a PyAny; - type IntoIter = PyListIterator<'a>; +impl<'a, 'py> IntoIterator for &'a Py2<'py, PyList> { + type Item = Py2<'py, PyAny>; + type IntoIter = PyListIterator2<'py>; fn into_iter(self) -> Self::IntoIter { self.iter() } } +impl<'py> IntoIterator for Py2<'py, PyList> { + type Item = Py2<'py, PyAny>; + type IntoIter = PyListIterator2<'py>; + + fn into_iter(self) -> Self::IntoIter { + PyListIterator2::new(self) + } +} + #[cfg(test)] mod tests { use crate::types::{PyList, PyTuple};