pyo3/types/
list.rs

1use std::iter::FusedIterator;
2
3use crate::err::{self, PyResult};
4use crate::ffi::{self, Py_ssize_t};
5use crate::ffi_ptr_ext::FfiPtrExt;
6use crate::internal_tricks::get_ssize_index;
7use crate::types::{PySequence, PyTuple};
8#[cfg(feature = "gil-refs")]
9use crate::PyNativeType;
10use crate::{Bound, PyAny, PyObject, Python, ToPyObject};
11
12use crate::types::any::PyAnyMethods;
13use crate::types::sequence::PySequenceMethods;
14
15/// Represents a Python `list`.
16///
17/// Values of this type are accessed via PyO3's smart pointers, e.g. as
18/// [`Py<PyList>`][crate::Py] or [`Bound<'py, PyList>`][Bound].
19///
20/// For APIs available on `list` objects, see the [`PyListMethods`] trait which is implemented for
21/// [`Bound<'py, PyDict>`][Bound].
22#[repr(transparent)]
23pub struct PyList(PyAny);
24
25pyobject_native_type_core!(PyList, pyobject_native_static_type_object!(ffi::PyList_Type), #checkfunction=ffi::PyList_Check);
26
27#[inline]
28#[track_caller]
29pub(crate) fn new_from_iter<'py>(
30    py: Python<'py>,
31    elements: &mut dyn ExactSizeIterator<Item = PyObject>,
32) -> Bound<'py, PyList> {
33    unsafe {
34        // PyList_New checks for overflow but has a bad error message, so we check ourselves
35        let len: Py_ssize_t = elements
36            .len()
37            .try_into()
38            .expect("out of range integral type conversion attempted on `elements.len()`");
39
40        let ptr = ffi::PyList_New(len);
41
42        // We create the `Bound` pointer here for two reasons:
43        // - panics if the ptr is null
44        // - its Drop cleans up the list if user code or the asserts panic.
45        let list = ptr.assume_owned(py).downcast_into_unchecked();
46
47        let mut counter: Py_ssize_t = 0;
48
49        for obj in elements.take(len as usize) {
50            #[cfg(not(Py_LIMITED_API))]
51            ffi::PyList_SET_ITEM(ptr, counter, obj.into_ptr());
52            #[cfg(Py_LIMITED_API)]
53            ffi::PyList_SetItem(ptr, counter, obj.into_ptr());
54            counter += 1;
55        }
56
57        assert!(elements.next().is_none(), "Attempted to create PyList but `elements` was larger than reported by its `ExactSizeIterator` implementation.");
58        assert_eq!(len, counter, "Attempted to create PyList but `elements` was smaller than reported by its `ExactSizeIterator` implementation.");
59
60        list
61    }
62}
63
64impl PyList {
65    /// Constructs a new list with the given elements.
66    ///
67    /// If you want to create a [`PyList`] with elements of different or unknown types, or from an
68    /// iterable that doesn't implement [`ExactSizeIterator`], use [`PyListMethods::append`].
69    ///
70    /// # Examples
71    ///
72    /// ```rust
73    /// use pyo3::prelude::*;
74    /// use pyo3::types::PyList;
75    ///
76    /// # fn main() {
77    /// Python::with_gil(|py| {
78    ///     let elements: Vec<i32> = vec![0, 1, 2, 3, 4, 5];
79    ///     let list = PyList::new_bound(py, elements);
80    ///     assert_eq!(format!("{:?}", list), "[0, 1, 2, 3, 4, 5]");
81    /// });
82    /// # }
83    /// ```
84    ///
85    /// # Panics
86    ///
87    /// This function will panic if `element`'s [`ExactSizeIterator`] implementation is incorrect.
88    /// All standard library structures implement this trait correctly, if they do, so calling this
89    /// function with (for example) [`Vec`]`<T>` or `&[T]` will always succeed.
90    #[track_caller]
91    pub fn new_bound<T, U>(
92        py: Python<'_>,
93        elements: impl IntoIterator<Item = T, IntoIter = U>,
94    ) -> Bound<'_, PyList>
95    where
96        T: ToPyObject,
97        U: ExactSizeIterator<Item = T>,
98    {
99        let mut iter = elements.into_iter().map(|e| e.to_object(py));
100        new_from_iter(py, &mut iter)
101    }
102
103    /// Constructs a new empty list.
104    pub fn empty_bound(py: Python<'_>) -> Bound<'_, PyList> {
105        unsafe {
106            ffi::PyList_New(0)
107                .assume_owned(py)
108                .downcast_into_unchecked()
109        }
110    }
111}
112
113#[cfg(feature = "gil-refs")]
114impl PyList {
115    /// Deprecated form of [`PyList::new_bound`].
116    #[inline]
117    #[track_caller]
118    #[deprecated(
119        since = "0.21.0",
120        note = "`PyList::new` will be replaced by `PyList::new_bound` in a future PyO3 version"
121    )]
122    pub fn new<T, U>(py: Python<'_>, elements: impl IntoIterator<Item = T, IntoIter = U>) -> &PyList
123    where
124        T: ToPyObject,
125        U: ExactSizeIterator<Item = T>,
126    {
127        Self::new_bound(py, elements).into_gil_ref()
128    }
129
130    /// Deprecated form of [`PyList::empty_bound`].
131    #[inline]
132    #[deprecated(
133        since = "0.21.0",
134        note = "`PyList::empty` will be replaced by `PyList::empty_bound` in a future PyO3 version"
135    )]
136    pub fn empty(py: Python<'_>) -> &PyList {
137        Self::empty_bound(py).into_gil_ref()
138    }
139
140    /// Returns the length of the list.
141    pub fn len(&self) -> usize {
142        self.as_borrowed().len()
143    }
144
145    /// Checks if the list is empty.
146    pub fn is_empty(&self) -> bool {
147        self.as_borrowed().is_empty()
148    }
149
150    /// Returns `self` cast as a `PySequence`.
151    pub fn as_sequence(&self) -> &PySequence {
152        unsafe { self.downcast_unchecked() }
153    }
154
155    /// Gets the list item at the specified index.
156    /// # Example
157    /// ```
158    /// use pyo3::{prelude::*, types::PyList};
159    /// Python::with_gil(|py| {
160    ///     let list = PyList::new_bound(py, [2, 3, 5, 7]);
161    ///     let obj = list.get_item(0);
162    ///     assert_eq!(obj.unwrap().extract::<i32>().unwrap(), 2);
163    /// });
164    /// ```
165    pub fn get_item(&self, index: usize) -> PyResult<&PyAny> {
166        self.as_borrowed().get_item(index).map(Bound::into_gil_ref)
167    }
168
169    /// Gets the list item at the specified index. Undefined behavior on bad index. Use with caution.
170    ///
171    /// # Safety
172    ///
173    /// Caller must verify that the index is within the bounds of the list.
174    #[cfg(not(Py_LIMITED_API))]
175    pub unsafe fn get_item_unchecked(&self, index: usize) -> &PyAny {
176        self.as_borrowed().get_item_unchecked(index).into_gil_ref()
177    }
178
179    /// Takes the slice `self[low:high]` and returns it as a new list.
180    ///
181    /// Indices must be nonnegative, and out-of-range indices are clipped to
182    /// `self.len()`.
183    pub fn get_slice(&self, low: usize, high: usize) -> &PyList {
184        self.as_borrowed().get_slice(low, high).into_gil_ref()
185    }
186
187    /// Sets the item at the specified index.
188    ///
189    /// Raises `IndexError` if the index is out of range.
190    pub fn set_item<I>(&self, index: usize, item: I) -> PyResult<()>
191    where
192        I: ToPyObject,
193    {
194        self.as_borrowed().set_item(index, item)
195    }
196
197    /// Deletes the `index`th element of self.
198    ///
199    /// This is equivalent to the Python statement `del self[i]`.
200    #[inline]
201    pub fn del_item(&self, index: usize) -> PyResult<()> {
202        self.as_borrowed().del_item(index)
203    }
204
205    /// Assigns the sequence `seq` to the slice of `self` from `low` to `high`.
206    ///
207    /// This is equivalent to the Python statement `self[low:high] = v`.
208    #[inline]
209    pub fn set_slice(&self, low: usize, high: usize, seq: &PyAny) -> PyResult<()> {
210        self.as_borrowed().set_slice(low, high, &seq.as_borrowed())
211    }
212
213    /// Deletes the slice from `low` to `high` from `self`.
214    ///
215    /// This is equivalent to the Python statement `del self[low:high]`.
216    #[inline]
217    pub fn del_slice(&self, low: usize, high: usize) -> PyResult<()> {
218        self.as_borrowed().del_slice(low, high)
219    }
220
221    /// Appends an item to the list.
222    pub fn append<I>(&self, item: I) -> PyResult<()>
223    where
224        I: ToPyObject,
225    {
226        self.as_borrowed().append(item)
227    }
228
229    /// Inserts an item at the specified index.
230    ///
231    /// If `index >= self.len()`, inserts at the end.
232    pub fn insert<I>(&self, index: usize, item: I) -> PyResult<()>
233    where
234        I: ToPyObject,
235    {
236        self.as_borrowed().insert(index, item)
237    }
238
239    /// Determines if self contains `value`.
240    ///
241    /// This is equivalent to the Python expression `value in self`.
242    #[inline]
243    pub fn contains<V>(&self, value: V) -> PyResult<bool>
244    where
245        V: ToPyObject,
246    {
247        self.as_borrowed().contains(value)
248    }
249
250    /// Returns the first index `i` for which `self[i] == value`.
251    ///
252    /// This is equivalent to the Python expression `self.index(value)`.
253    #[inline]
254    pub fn index<V>(&self, value: V) -> PyResult<usize>
255    where
256        V: ToPyObject,
257    {
258        self.as_borrowed().index(value)
259    }
260
261    /// Returns an iterator over this list's items.
262    pub fn iter(&self) -> PyListIterator<'_> {
263        PyListIterator(self.as_borrowed().iter())
264    }
265
266    /// Sorts the list in-place. Equivalent to the Python expression `l.sort()`.
267    pub fn sort(&self) -> PyResult<()> {
268        self.as_borrowed().sort()
269    }
270
271    /// Reverses the list in-place. Equivalent to the Python expression `l.reverse()`.
272    pub fn reverse(&self) -> PyResult<()> {
273        self.as_borrowed().reverse()
274    }
275
276    /// Return a new tuple containing the contents of the list; equivalent to the Python expression `tuple(list)`.
277    ///
278    /// This method is equivalent to `self.as_sequence().to_tuple()` and faster than `PyTuple::new(py, this_list)`.
279    pub fn to_tuple(&self) -> &PyTuple {
280        self.as_borrowed().to_tuple().into_gil_ref()
281    }
282}
283
284#[cfg(feature = "gil-refs")]
285index_impls!(PyList, "list", PyList::len, PyList::get_slice);
286
287/// Implementation of functionality for [`PyList`].
288///
289/// These methods are defined for the `Bound<'py, PyList>` smart pointer, so to use method call
290/// syntax these methods are separated into a trait, because stable Rust does not yet support
291/// `arbitrary_self_types`.
292#[doc(alias = "PyList")]
293pub trait PyListMethods<'py>: crate::sealed::Sealed {
294    /// Returns the length of the list.
295    fn len(&self) -> usize;
296
297    /// Checks if the list is empty.
298    fn is_empty(&self) -> bool;
299
300    /// Returns `self` cast as a `PySequence`.
301    fn as_sequence(&self) -> &Bound<'py, PySequence>;
302
303    /// Returns `self` cast as a `PySequence`.
304    fn into_sequence(self) -> Bound<'py, PySequence>;
305
306    /// Gets the list item at the specified index.
307    /// # Example
308    /// ```
309    /// use pyo3::{prelude::*, types::PyList};
310    /// Python::with_gil(|py| {
311    ///     let list = PyList::new_bound(py, [2, 3, 5, 7]);
312    ///     let obj = list.get_item(0);
313    ///     assert_eq!(obj.unwrap().extract::<i32>().unwrap(), 2);
314    /// });
315    /// ```
316    fn get_item(&self, index: usize) -> PyResult<Bound<'py, PyAny>>;
317
318    /// Gets the list item at the specified index. Undefined behavior on bad index. Use with caution.
319    ///
320    /// # Safety
321    ///
322    /// Caller must verify that the index is within the bounds of the list.
323    #[cfg(not(Py_LIMITED_API))]
324    unsafe fn get_item_unchecked(&self, index: usize) -> Bound<'py, PyAny>;
325
326    /// Takes the slice `self[low:high]` and returns it as a new list.
327    ///
328    /// Indices must be nonnegative, and out-of-range indices are clipped to
329    /// `self.len()`.
330    fn get_slice(&self, low: usize, high: usize) -> Bound<'py, PyList>;
331
332    /// Sets the item at the specified index.
333    ///
334    /// Raises `IndexError` if the index is out of range.
335    fn set_item<I>(&self, index: usize, item: I) -> PyResult<()>
336    where
337        I: ToPyObject;
338
339    /// Deletes the `index`th element of self.
340    ///
341    /// This is equivalent to the Python statement `del self[i]`.
342    fn del_item(&self, index: usize) -> PyResult<()>;
343
344    /// Assigns the sequence `seq` to the slice of `self` from `low` to `high`.
345    ///
346    /// This is equivalent to the Python statement `self[low:high] = v`.
347    fn set_slice(&self, low: usize, high: usize, seq: &Bound<'_, PyAny>) -> PyResult<()>;
348
349    /// Deletes the slice from `low` to `high` from `self`.
350    ///
351    /// This is equivalent to the Python statement `del self[low:high]`.
352    fn del_slice(&self, low: usize, high: usize) -> PyResult<()>;
353
354    /// Appends an item to the list.
355    fn append<I>(&self, item: I) -> PyResult<()>
356    where
357        I: ToPyObject;
358
359    /// Inserts an item at the specified index.
360    ///
361    /// If `index >= self.len()`, inserts at the end.
362    fn insert<I>(&self, index: usize, item: I) -> PyResult<()>
363    where
364        I: ToPyObject;
365
366    /// Determines if self contains `value`.
367    ///
368    /// This is equivalent to the Python expression `value in self`.
369    fn contains<V>(&self, value: V) -> PyResult<bool>
370    where
371        V: ToPyObject;
372
373    /// Returns the first index `i` for which `self[i] == value`.
374    ///
375    /// This is equivalent to the Python expression `self.index(value)`.
376    fn index<V>(&self, value: V) -> PyResult<usize>
377    where
378        V: ToPyObject;
379
380    /// Returns an iterator over this list's items.
381    fn iter(&self) -> BoundListIterator<'py>;
382
383    /// Sorts the list in-place. Equivalent to the Python expression `l.sort()`.
384    fn sort(&self) -> PyResult<()>;
385
386    /// Reverses the list in-place. Equivalent to the Python expression `l.reverse()`.
387    fn reverse(&self) -> PyResult<()>;
388
389    /// Return a new tuple containing the contents of the list; equivalent to the Python expression `tuple(list)`.
390    ///
391    /// This method is equivalent to `self.as_sequence().to_tuple()` and faster than `PyTuple::new(py, this_list)`.
392    fn to_tuple(&self) -> Bound<'py, PyTuple>;
393}
394
395impl<'py> PyListMethods<'py> for Bound<'py, PyList> {
396    /// Returns the length of the list.
397    fn len(&self) -> usize {
398        unsafe {
399            #[cfg(not(Py_LIMITED_API))]
400            let size = ffi::PyList_GET_SIZE(self.as_ptr());
401            #[cfg(Py_LIMITED_API)]
402            let size = ffi::PyList_Size(self.as_ptr());
403
404            // non-negative Py_ssize_t should always fit into Rust usize
405            size as usize
406        }
407    }
408
409    /// Checks if the list is empty.
410    fn is_empty(&self) -> bool {
411        self.len() == 0
412    }
413
414    /// Returns `self` cast as a `PySequence`.
415    fn as_sequence(&self) -> &Bound<'py, PySequence> {
416        unsafe { self.downcast_unchecked() }
417    }
418
419    /// Returns `self` cast as a `PySequence`.
420    fn into_sequence(self) -> Bound<'py, PySequence> {
421        unsafe { self.into_any().downcast_into_unchecked() }
422    }
423
424    /// Gets the list item at the specified index.
425    /// # Example
426    /// ```
427    /// use pyo3::{prelude::*, types::PyList};
428    /// Python::with_gil(|py| {
429    ///     let list = PyList::new_bound(py, [2, 3, 5, 7]);
430    ///     let obj = list.get_item(0);
431    ///     assert_eq!(obj.unwrap().extract::<i32>().unwrap(), 2);
432    /// });
433    /// ```
434    fn get_item(&self, index: usize) -> PyResult<Bound<'py, PyAny>> {
435        unsafe {
436            ffi::compat::PyList_GetItemRef(self.as_ptr(), index as Py_ssize_t)
437                .assume_owned_or_err(self.py())
438        }
439    }
440
441    /// Gets the list item at the specified index. Undefined behavior on bad index. Use with caution.
442    ///
443    /// # Safety
444    ///
445    /// Caller must verify that the index is within the bounds of the list.
446    #[cfg(not(Py_LIMITED_API))]
447    unsafe fn get_item_unchecked(&self, index: usize) -> Bound<'py, PyAny> {
448        // PyList_GET_ITEM return borrowed ptr; must make owned for safety (see #890).
449        ffi::PyList_GET_ITEM(self.as_ptr(), index as Py_ssize_t)
450            .assume_borrowed(self.py())
451            .to_owned()
452    }
453
454    /// Takes the slice `self[low:high]` and returns it as a new list.
455    ///
456    /// Indices must be nonnegative, and out-of-range indices are clipped to
457    /// `self.len()`.
458    fn get_slice(&self, low: usize, high: usize) -> Bound<'py, PyList> {
459        unsafe {
460            ffi::PyList_GetSlice(self.as_ptr(), get_ssize_index(low), get_ssize_index(high))
461                .assume_owned(self.py())
462                .downcast_into_unchecked()
463        }
464    }
465
466    /// Sets the item at the specified index.
467    ///
468    /// Raises `IndexError` if the index is out of range.
469    fn set_item<I>(&self, index: usize, item: I) -> PyResult<()>
470    where
471        I: ToPyObject,
472    {
473        fn inner(list: &Bound<'_, PyList>, index: usize, item: Bound<'_, PyAny>) -> PyResult<()> {
474            err::error_on_minusone(list.py(), unsafe {
475                ffi::PyList_SetItem(list.as_ptr(), get_ssize_index(index), item.into_ptr())
476            })
477        }
478
479        let py = self.py();
480        inner(self, index, item.to_object(py).into_bound(py))
481    }
482
483    /// Deletes the `index`th element of self.
484    ///
485    /// This is equivalent to the Python statement `del self[i]`.
486    #[inline]
487    fn del_item(&self, index: usize) -> PyResult<()> {
488        self.as_sequence().del_item(index)
489    }
490
491    /// Assigns the sequence `seq` to the slice of `self` from `low` to `high`.
492    ///
493    /// This is equivalent to the Python statement `self[low:high] = v`.
494    #[inline]
495    fn set_slice(&self, low: usize, high: usize, seq: &Bound<'_, PyAny>) -> PyResult<()> {
496        err::error_on_minusone(self.py(), unsafe {
497            ffi::PyList_SetSlice(
498                self.as_ptr(),
499                get_ssize_index(low),
500                get_ssize_index(high),
501                seq.as_ptr(),
502            )
503        })
504    }
505
506    /// Deletes the slice from `low` to `high` from `self`.
507    ///
508    /// This is equivalent to the Python statement `del self[low:high]`.
509    #[inline]
510    fn del_slice(&self, low: usize, high: usize) -> PyResult<()> {
511        self.as_sequence().del_slice(low, high)
512    }
513
514    /// Appends an item to the list.
515    fn append<I>(&self, item: I) -> PyResult<()>
516    where
517        I: ToPyObject,
518    {
519        fn inner(list: &Bound<'_, PyList>, item: Bound<'_, PyAny>) -> PyResult<()> {
520            err::error_on_minusone(list.py(), unsafe {
521                ffi::PyList_Append(list.as_ptr(), item.as_ptr())
522            })
523        }
524
525        let py = self.py();
526        inner(self, item.to_object(py).into_bound(py))
527    }
528
529    /// Inserts an item at the specified index.
530    ///
531    /// If `index >= self.len()`, inserts at the end.
532    fn insert<I>(&self, index: usize, item: I) -> PyResult<()>
533    where
534        I: ToPyObject,
535    {
536        fn inner(list: &Bound<'_, PyList>, index: usize, item: Bound<'_, PyAny>) -> PyResult<()> {
537            err::error_on_minusone(list.py(), unsafe {
538                ffi::PyList_Insert(list.as_ptr(), get_ssize_index(index), item.as_ptr())
539            })
540        }
541
542        let py = self.py();
543        inner(self, index, item.to_object(py).into_bound(py))
544    }
545
546    /// Determines if self contains `value`.
547    ///
548    /// This is equivalent to the Python expression `value in self`.
549    #[inline]
550    fn contains<V>(&self, value: V) -> PyResult<bool>
551    where
552        V: ToPyObject,
553    {
554        self.as_sequence().contains(value)
555    }
556
557    /// Returns the first index `i` for which `self[i] == value`.
558    ///
559    /// This is equivalent to the Python expression `self.index(value)`.
560    #[inline]
561    fn index<V>(&self, value: V) -> PyResult<usize>
562    where
563        V: ToPyObject,
564    {
565        self.as_sequence().index(value)
566    }
567
568    /// Returns an iterator over this list's items.
569    fn iter(&self) -> BoundListIterator<'py> {
570        BoundListIterator::new(self.clone())
571    }
572
573    /// Sorts the list in-place. Equivalent to the Python expression `l.sort()`.
574    fn sort(&self) -> PyResult<()> {
575        err::error_on_minusone(self.py(), unsafe { ffi::PyList_Sort(self.as_ptr()) })
576    }
577
578    /// Reverses the list in-place. Equivalent to the Python expression `l.reverse()`.
579    fn reverse(&self) -> PyResult<()> {
580        err::error_on_minusone(self.py(), unsafe { ffi::PyList_Reverse(self.as_ptr()) })
581    }
582
583    /// Return a new tuple containing the contents of the list; equivalent to the Python expression `tuple(list)`.
584    ///
585    /// This method is equivalent to `self.as_sequence().to_tuple()` and faster than `PyTuple::new(py, this_list)`.
586    fn to_tuple(&self) -> Bound<'py, PyTuple> {
587        unsafe {
588            ffi::PyList_AsTuple(self.as_ptr())
589                .assume_owned(self.py())
590                .downcast_into_unchecked()
591        }
592    }
593}
594
595/// Used by `PyList::iter()`.
596#[cfg(feature = "gil-refs")]
597pub struct PyListIterator<'a>(BoundListIterator<'a>);
598
599#[cfg(feature = "gil-refs")]
600impl<'a> Iterator for PyListIterator<'a> {
601    type Item = &'a PyAny;
602
603    #[inline]
604    fn next(&mut self) -> Option<Self::Item> {
605        self.0.next().map(Bound::into_gil_ref)
606    }
607
608    #[inline]
609    fn size_hint(&self) -> (usize, Option<usize>) {
610        self.0.size_hint()
611    }
612}
613
614#[cfg(feature = "gil-refs")]
615impl<'a> DoubleEndedIterator for PyListIterator<'a> {
616    #[inline]
617    fn next_back(&mut self) -> Option<Self::Item> {
618        self.0.next_back().map(Bound::into_gil_ref)
619    }
620}
621
622#[cfg(feature = "gil-refs")]
623impl<'a> ExactSizeIterator for PyListIterator<'a> {
624    fn len(&self) -> usize {
625        self.0.len()
626    }
627}
628
629#[cfg(feature = "gil-refs")]
630impl FusedIterator for PyListIterator<'_> {}
631
632#[cfg(feature = "gil-refs")]
633impl<'a> IntoIterator for &'a PyList {
634    type Item = &'a PyAny;
635    type IntoIter = PyListIterator<'a>;
636
637    fn into_iter(self) -> Self::IntoIter {
638        self.iter()
639    }
640}
641
642/// Used by `PyList::iter()`.
643pub struct BoundListIterator<'py> {
644    list: Bound<'py, PyList>,
645    index: usize,
646    length: usize,
647}
648
649impl<'py> BoundListIterator<'py> {
650    fn new(list: Bound<'py, PyList>) -> Self {
651        let length: usize = list.len();
652        BoundListIterator {
653            list,
654            index: 0,
655            length,
656        }
657    }
658
659    unsafe fn get_item(&self, index: usize) -> Bound<'py, PyAny> {
660        #[cfg(any(Py_LIMITED_API, PyPy))]
661        let item = self.list.get_item(index).expect("list.get failed");
662        #[cfg(not(any(Py_LIMITED_API, PyPy)))]
663        let item = self.list.get_item_unchecked(index);
664        item
665    }
666}
667
668impl<'py> Iterator for BoundListIterator<'py> {
669    type Item = Bound<'py, PyAny>;
670
671    #[inline]
672    fn next(&mut self) -> Option<Self::Item> {
673        let length = self.length.min(self.list.len());
674
675        if self.index < length {
676            let item = unsafe { self.get_item(self.index) };
677            self.index += 1;
678            Some(item)
679        } else {
680            None
681        }
682    }
683
684    #[inline]
685    fn size_hint(&self) -> (usize, Option<usize>) {
686        let len = self.len();
687        (len, Some(len))
688    }
689}
690
691impl DoubleEndedIterator for BoundListIterator<'_> {
692    #[inline]
693    fn next_back(&mut self) -> Option<Self::Item> {
694        let length = self.length.min(self.list.len());
695
696        if self.index < length {
697            let item = unsafe { self.get_item(length - 1) };
698            self.length = length - 1;
699            Some(item)
700        } else {
701            None
702        }
703    }
704}
705
706impl ExactSizeIterator for BoundListIterator<'_> {
707    fn len(&self) -> usize {
708        self.length.saturating_sub(self.index)
709    }
710}
711
712impl FusedIterator for BoundListIterator<'_> {}
713
714impl<'py> IntoIterator for Bound<'py, PyList> {
715    type Item = Bound<'py, PyAny>;
716    type IntoIter = BoundListIterator<'py>;
717
718    fn into_iter(self) -> Self::IntoIter {
719        BoundListIterator::new(self)
720    }
721}
722
723impl<'py> IntoIterator for &Bound<'py, PyList> {
724    type Item = Bound<'py, PyAny>;
725    type IntoIter = BoundListIterator<'py>;
726
727    fn into_iter(self) -> Self::IntoIter {
728        self.iter()
729    }
730}
731
732#[cfg(test)]
733mod tests {
734    use crate::types::any::PyAnyMethods;
735    use crate::types::list::PyListMethods;
736    use crate::types::sequence::PySequenceMethods;
737    use crate::types::{PyList, PyTuple};
738    use crate::Python;
739    use crate::{IntoPy, PyObject, ToPyObject};
740
741    #[test]
742    fn test_new() {
743        Python::with_gil(|py| {
744            let list = PyList::new_bound(py, [2, 3, 5, 7]);
745            assert_eq!(2, list.get_item(0).unwrap().extract::<i32>().unwrap());
746            assert_eq!(3, list.get_item(1).unwrap().extract::<i32>().unwrap());
747            assert_eq!(5, list.get_item(2).unwrap().extract::<i32>().unwrap());
748            assert_eq!(7, list.get_item(3).unwrap().extract::<i32>().unwrap());
749        });
750    }
751
752    #[test]
753    fn test_len() {
754        Python::with_gil(|py| {
755            let list = PyList::new_bound(py, [1, 2, 3, 4]);
756            assert_eq!(4, list.len());
757        });
758    }
759
760    #[test]
761    fn test_get_item() {
762        Python::with_gil(|py| {
763            let list = PyList::new_bound(py, [2, 3, 5, 7]);
764            assert_eq!(2, list.get_item(0).unwrap().extract::<i32>().unwrap());
765            assert_eq!(3, list.get_item(1).unwrap().extract::<i32>().unwrap());
766            assert_eq!(5, list.get_item(2).unwrap().extract::<i32>().unwrap());
767            assert_eq!(7, list.get_item(3).unwrap().extract::<i32>().unwrap());
768        });
769    }
770
771    #[test]
772    fn test_get_slice() {
773        Python::with_gil(|py| {
774            let list = PyList::new_bound(py, [2, 3, 5, 7]);
775            let slice = list.get_slice(1, 3);
776            assert_eq!(2, slice.len());
777            let slice = list.get_slice(1, 7);
778            assert_eq!(3, slice.len());
779        });
780    }
781
782    #[test]
783    fn test_set_item() {
784        Python::with_gil(|py| {
785            let list = PyList::new_bound(py, [2, 3, 5, 7]);
786            let val = 42i32.to_object(py);
787            let val2 = 42i32.to_object(py);
788            assert_eq!(2, list.get_item(0).unwrap().extract::<i32>().unwrap());
789            list.set_item(0, val).unwrap();
790            assert_eq!(42, list.get_item(0).unwrap().extract::<i32>().unwrap());
791            assert!(list.set_item(10, val2).is_err());
792        });
793    }
794
795    #[test]
796    fn test_set_item_refcnt() {
797        Python::with_gil(|py| {
798            let obj = py.eval_bound("object()", None, None).unwrap();
799            let cnt;
800            {
801                let v = vec![2];
802                let ob = v.to_object(py);
803                let list = ob.downcast_bound::<PyList>(py).unwrap();
804                cnt = obj.get_refcnt();
805                list.set_item(0, &obj).unwrap();
806            }
807
808            assert_eq!(cnt, obj.get_refcnt());
809        });
810    }
811
812    #[test]
813    fn test_insert() {
814        Python::with_gil(|py| {
815            let list = PyList::new_bound(py, [2, 3, 5, 7]);
816            let val = 42i32.to_object(py);
817            let val2 = 43i32.to_object(py);
818            assert_eq!(4, list.len());
819            assert_eq!(2, list.get_item(0).unwrap().extract::<i32>().unwrap());
820            list.insert(0, val).unwrap();
821            list.insert(1000, val2).unwrap();
822            assert_eq!(6, list.len());
823            assert_eq!(42, list.get_item(0).unwrap().extract::<i32>().unwrap());
824            assert_eq!(2, list.get_item(1).unwrap().extract::<i32>().unwrap());
825            assert_eq!(43, list.get_item(5).unwrap().extract::<i32>().unwrap());
826        });
827    }
828
829    #[test]
830    fn test_insert_refcnt() {
831        Python::with_gil(|py| {
832            let cnt;
833            let obj = py.eval_bound("object()", None, None).unwrap();
834            {
835                let list = PyList::empty_bound(py);
836                cnt = obj.get_refcnt();
837                list.insert(0, &obj).unwrap();
838            }
839
840            assert_eq!(cnt, obj.get_refcnt());
841        });
842    }
843
844    #[test]
845    fn test_append() {
846        Python::with_gil(|py| {
847            let list = PyList::new_bound(py, [2]);
848            list.append(3).unwrap();
849            assert_eq!(2, list.get_item(0).unwrap().extract::<i32>().unwrap());
850            assert_eq!(3, list.get_item(1).unwrap().extract::<i32>().unwrap());
851        });
852    }
853
854    #[test]
855    fn test_append_refcnt() {
856        Python::with_gil(|py| {
857            let cnt;
858            let obj = py.eval_bound("object()", None, None).unwrap();
859            {
860                let list = PyList::empty_bound(py);
861                cnt = obj.get_refcnt();
862                list.append(&obj).unwrap();
863            }
864            assert_eq!(cnt, obj.get_refcnt());
865        });
866    }
867
868    #[test]
869    fn test_iter() {
870        Python::with_gil(|py| {
871            let v = vec![2, 3, 5, 7];
872            let list = PyList::new_bound(py, &v);
873            let mut idx = 0;
874            for el in list {
875                assert_eq!(v[idx], el.extract::<i32>().unwrap());
876                idx += 1;
877            }
878            assert_eq!(idx, v.len());
879        });
880    }
881
882    #[test]
883    fn test_iter_size_hint() {
884        Python::with_gil(|py| {
885            let v = vec![2, 3, 5, 7];
886            let ob = v.to_object(py);
887            let list = ob.downcast_bound::<PyList>(py).unwrap();
888
889            let mut iter = list.iter();
890            assert_eq!(iter.size_hint(), (v.len(), Some(v.len())));
891            iter.next();
892            assert_eq!(iter.size_hint(), (v.len() - 1, Some(v.len() - 1)));
893
894            // Exhaust iterator.
895            for _ in &mut iter {}
896
897            assert_eq!(iter.size_hint(), (0, Some(0)));
898        });
899    }
900
901    #[test]
902    fn test_iter_rev() {
903        Python::with_gil(|py| {
904            let v = vec![2, 3, 5, 7];
905            let ob = v.to_object(py);
906            let list = ob.downcast_bound::<PyList>(py).unwrap();
907
908            let mut iter = list.iter().rev();
909
910            assert_eq!(iter.size_hint(), (4, Some(4)));
911
912            assert_eq!(iter.next().unwrap().extract::<i32>().unwrap(), 7);
913            assert_eq!(iter.size_hint(), (3, Some(3)));
914
915            assert_eq!(iter.next().unwrap().extract::<i32>().unwrap(), 5);
916            assert_eq!(iter.size_hint(), (2, Some(2)));
917
918            assert_eq!(iter.next().unwrap().extract::<i32>().unwrap(), 3);
919            assert_eq!(iter.size_hint(), (1, Some(1)));
920
921            assert_eq!(iter.next().unwrap().extract::<i32>().unwrap(), 2);
922            assert_eq!(iter.size_hint(), (0, Some(0)));
923
924            assert!(iter.next().is_none());
925            assert!(iter.next().is_none());
926        });
927    }
928
929    #[test]
930    fn test_into_iter() {
931        Python::with_gil(|py| {
932            let list = PyList::new_bound(py, [1, 2, 3, 4]);
933            for (i, item) in list.iter().enumerate() {
934                assert_eq!((i + 1) as i32, item.extract::<i32>().unwrap());
935            }
936        });
937    }
938
939    #[test]
940    fn test_into_iter_bound() {
941        use crate::types::any::PyAnyMethods;
942
943        Python::with_gil(|py| {
944            let list = PyList::new_bound(py, [1, 2, 3, 4]);
945            let mut items = vec![];
946            for item in &list {
947                items.push(item.extract::<i32>().unwrap());
948            }
949            assert_eq!(items, vec![1, 2, 3, 4]);
950        });
951    }
952
953    #[test]
954    fn test_as_sequence() {
955        Python::with_gil(|py| {
956            let list = PyList::new_bound(py, [1, 2, 3, 4]);
957
958            assert_eq!(list.as_sequence().len().unwrap(), 4);
959            assert_eq!(
960                list.as_sequence()
961                    .get_item(1)
962                    .unwrap()
963                    .extract::<i32>()
964                    .unwrap(),
965                2
966            );
967        });
968    }
969
970    #[test]
971    fn test_into_sequence() {
972        Python::with_gil(|py| {
973            let list = PyList::new_bound(py, [1, 2, 3, 4]);
974
975            let sequence = list.into_sequence();
976
977            assert_eq!(sequence.len().unwrap(), 4);
978            assert_eq!(sequence.get_item(1).unwrap().extract::<i32>().unwrap(), 2);
979        });
980    }
981
982    #[test]
983    fn test_extract() {
984        Python::with_gil(|py| {
985            let v = vec![2, 3, 5, 7];
986            let list = PyList::new_bound(py, &v);
987            let v2 = list.as_ref().extract::<Vec<i32>>().unwrap();
988            assert_eq!(v, v2);
989        });
990    }
991
992    #[test]
993    fn test_sort() {
994        Python::with_gil(|py| {
995            let v = vec![7, 3, 2, 5];
996            let list = PyList::new_bound(py, &v);
997            assert_eq!(7, list.get_item(0).unwrap().extract::<i32>().unwrap());
998            assert_eq!(3, list.get_item(1).unwrap().extract::<i32>().unwrap());
999            assert_eq!(2, list.get_item(2).unwrap().extract::<i32>().unwrap());
1000            assert_eq!(5, list.get_item(3).unwrap().extract::<i32>().unwrap());
1001            list.sort().unwrap();
1002            assert_eq!(2, list.get_item(0).unwrap().extract::<i32>().unwrap());
1003            assert_eq!(3, list.get_item(1).unwrap().extract::<i32>().unwrap());
1004            assert_eq!(5, list.get_item(2).unwrap().extract::<i32>().unwrap());
1005            assert_eq!(7, list.get_item(3).unwrap().extract::<i32>().unwrap());
1006        });
1007    }
1008
1009    #[test]
1010    fn test_reverse() {
1011        Python::with_gil(|py| {
1012            let v = vec![2, 3, 5, 7];
1013            let list = PyList::new_bound(py, &v);
1014            assert_eq!(2, list.get_item(0).unwrap().extract::<i32>().unwrap());
1015            assert_eq!(3, list.get_item(1).unwrap().extract::<i32>().unwrap());
1016            assert_eq!(5, list.get_item(2).unwrap().extract::<i32>().unwrap());
1017            assert_eq!(7, list.get_item(3).unwrap().extract::<i32>().unwrap());
1018            list.reverse().unwrap();
1019            assert_eq!(7, list.get_item(0).unwrap().extract::<i32>().unwrap());
1020            assert_eq!(5, list.get_item(1).unwrap().extract::<i32>().unwrap());
1021            assert_eq!(3, list.get_item(2).unwrap().extract::<i32>().unwrap());
1022            assert_eq!(2, list.get_item(3).unwrap().extract::<i32>().unwrap());
1023        });
1024    }
1025
1026    #[test]
1027    fn test_array_into_py() {
1028        Python::with_gil(|py| {
1029            let array: PyObject = [1, 2].into_py(py);
1030            let list = array.downcast_bound::<PyList>(py).unwrap();
1031            assert_eq!(1, list.get_item(0).unwrap().extract::<i32>().unwrap());
1032            assert_eq!(2, list.get_item(1).unwrap().extract::<i32>().unwrap());
1033        });
1034    }
1035
1036    #[test]
1037    fn test_list_get_item_invalid_index() {
1038        Python::with_gil(|py| {
1039            let list = PyList::new_bound(py, [2, 3, 5, 7]);
1040            let obj = list.get_item(5);
1041            assert!(obj.is_err());
1042            assert_eq!(
1043                obj.unwrap_err().to_string(),
1044                "IndexError: list index out of range"
1045            );
1046        });
1047    }
1048
1049    #[test]
1050    fn test_list_get_item_sanity() {
1051        Python::with_gil(|py| {
1052            let list = PyList::new_bound(py, [2, 3, 5, 7]);
1053            let obj = list.get_item(0);
1054            assert_eq!(obj.unwrap().extract::<i32>().unwrap(), 2);
1055        });
1056    }
1057
1058    #[cfg(not(any(Py_LIMITED_API, PyPy)))]
1059    #[test]
1060    fn test_list_get_item_unchecked_sanity() {
1061        Python::with_gil(|py| {
1062            let list = PyList::new_bound(py, [2, 3, 5, 7]);
1063            let obj = unsafe { list.get_item_unchecked(0) };
1064            assert_eq!(obj.extract::<i32>().unwrap(), 2);
1065        });
1066    }
1067
1068    #[test]
1069    #[cfg(feature = "gil-refs")]
1070    #[allow(deprecated)]
1071    fn test_list_index_trait() {
1072        Python::with_gil(|py| {
1073            let list = PyList::new(py, [2, 3, 5]);
1074            assert_eq!(2, list[0].extract::<i32>().unwrap());
1075            assert_eq!(3, list[1].extract::<i32>().unwrap());
1076            assert_eq!(5, list[2].extract::<i32>().unwrap());
1077        });
1078    }
1079
1080    #[test]
1081    #[should_panic]
1082    #[cfg(feature = "gil-refs")]
1083    #[allow(deprecated)]
1084    fn test_list_index_trait_panic() {
1085        Python::with_gil(|py| {
1086            let list = PyList::new(py, [2, 3, 5]);
1087            let _ = &list[7];
1088        });
1089    }
1090
1091    #[test]
1092    #[cfg(feature = "gil-refs")]
1093    #[allow(deprecated)]
1094    fn test_list_index_trait_ranges() {
1095        Python::with_gil(|py| {
1096            let list = PyList::new(py, [2, 3, 5]);
1097            assert_eq!(vec![3, 5], list[1..3].extract::<Vec<i32>>().unwrap());
1098            assert_eq!(Vec::<i32>::new(), list[3..3].extract::<Vec<i32>>().unwrap());
1099            assert_eq!(vec![3, 5], list[1..].extract::<Vec<i32>>().unwrap());
1100            assert_eq!(Vec::<i32>::new(), list[3..].extract::<Vec<i32>>().unwrap());
1101            assert_eq!(vec![2, 3, 5], list[..].extract::<Vec<i32>>().unwrap());
1102            assert_eq!(vec![3, 5], list[1..=2].extract::<Vec<i32>>().unwrap());
1103            assert_eq!(vec![2, 3], list[..2].extract::<Vec<i32>>().unwrap());
1104            assert_eq!(vec![2, 3], list[..=1].extract::<Vec<i32>>().unwrap());
1105        })
1106    }
1107
1108    #[test]
1109    #[should_panic = "range start index 5 out of range for list of length 3"]
1110    #[cfg(feature = "gil-refs")]
1111    #[allow(deprecated)]
1112    fn test_list_index_trait_range_panic_start() {
1113        Python::with_gil(|py| {
1114            let list = PyList::new(py, [2, 3, 5]);
1115            list[5..10].extract::<Vec<i32>>().unwrap();
1116        })
1117    }
1118
1119    #[test]
1120    #[should_panic = "range end index 10 out of range for list of length 3"]
1121    #[cfg(feature = "gil-refs")]
1122    #[allow(deprecated)]
1123    fn test_list_index_trait_range_panic_end() {
1124        Python::with_gil(|py| {
1125            let list = PyList::new(py, [2, 3, 5]);
1126            list[1..10].extract::<Vec<i32>>().unwrap();
1127        })
1128    }
1129
1130    #[test]
1131    #[should_panic = "slice index starts at 2 but ends at 1"]
1132    #[cfg(feature = "gil-refs")]
1133    #[allow(deprecated)]
1134    fn test_list_index_trait_range_panic_wrong_order() {
1135        Python::with_gil(|py| {
1136            let list = PyList::new(py, [2, 3, 5]);
1137            #[allow(clippy::reversed_empty_ranges)]
1138            list[2..1].extract::<Vec<i32>>().unwrap();
1139        })
1140    }
1141
1142    #[test]
1143    #[should_panic = "range start index 8 out of range for list of length 3"]
1144    #[cfg(feature = "gil-refs")]
1145    #[allow(deprecated)]
1146    fn test_list_index_trait_range_from_panic() {
1147        Python::with_gil(|py| {
1148            let list = PyList::new(py, [2, 3, 5]);
1149            list[8..].extract::<Vec<i32>>().unwrap();
1150        })
1151    }
1152
1153    #[test]
1154    fn test_list_del_item() {
1155        Python::with_gil(|py| {
1156            let list = PyList::new_bound(py, [1, 1, 2, 3, 5, 8]);
1157            assert!(list.del_item(10).is_err());
1158            assert_eq!(1, list.get_item(0).unwrap().extract::<i32>().unwrap());
1159            assert!(list.del_item(0).is_ok());
1160            assert_eq!(1, list.get_item(0).unwrap().extract::<i32>().unwrap());
1161            assert!(list.del_item(0).is_ok());
1162            assert_eq!(2, list.get_item(0).unwrap().extract::<i32>().unwrap());
1163            assert!(list.del_item(0).is_ok());
1164            assert_eq!(3, list.get_item(0).unwrap().extract::<i32>().unwrap());
1165            assert!(list.del_item(0).is_ok());
1166            assert_eq!(5, list.get_item(0).unwrap().extract::<i32>().unwrap());
1167            assert!(list.del_item(0).is_ok());
1168            assert_eq!(8, list.get_item(0).unwrap().extract::<i32>().unwrap());
1169            assert!(list.del_item(0).is_ok());
1170            assert_eq!(0, list.len());
1171            assert!(list.del_item(0).is_err());
1172        });
1173    }
1174
1175    #[test]
1176    fn test_list_set_slice() {
1177        Python::with_gil(|py| {
1178            let list = PyList::new_bound(py, [1, 1, 2, 3, 5, 8]);
1179            let ins = PyList::new_bound(py, [7, 4]);
1180            list.set_slice(1, 4, &ins).unwrap();
1181            assert_eq!([1, 7, 4, 5, 8], list.extract::<[i32; 5]>().unwrap());
1182            list.set_slice(3, 100, &PyList::empty_bound(py)).unwrap();
1183            assert_eq!([1, 7, 4], list.extract::<[i32; 3]>().unwrap());
1184        });
1185    }
1186
1187    #[test]
1188    fn test_list_del_slice() {
1189        Python::with_gil(|py| {
1190            let list = PyList::new_bound(py, [1, 1, 2, 3, 5, 8]);
1191            list.del_slice(1, 4).unwrap();
1192            assert_eq!([1, 5, 8], list.extract::<[i32; 3]>().unwrap());
1193            list.del_slice(1, 100).unwrap();
1194            assert_eq!([1], list.extract::<[i32; 1]>().unwrap());
1195        });
1196    }
1197
1198    #[test]
1199    fn test_list_contains() {
1200        Python::with_gil(|py| {
1201            let list = PyList::new_bound(py, [1, 1, 2, 3, 5, 8]);
1202            assert_eq!(6, list.len());
1203
1204            let bad_needle = 7i32.to_object(py);
1205            assert!(!list.contains(&bad_needle).unwrap());
1206
1207            let good_needle = 8i32.to_object(py);
1208            assert!(list.contains(&good_needle).unwrap());
1209
1210            let type_coerced_needle = 8f32.to_object(py);
1211            assert!(list.contains(&type_coerced_needle).unwrap());
1212        });
1213    }
1214
1215    #[test]
1216    fn test_list_index() {
1217        Python::with_gil(|py| {
1218            let list = PyList::new_bound(py, [1, 1, 2, 3, 5, 8]);
1219            assert_eq!(0, list.index(1i32).unwrap());
1220            assert_eq!(2, list.index(2i32).unwrap());
1221            assert_eq!(3, list.index(3i32).unwrap());
1222            assert_eq!(4, list.index(5i32).unwrap());
1223            assert_eq!(5, list.index(8i32).unwrap());
1224            assert!(list.index(42i32).is_err());
1225        });
1226    }
1227
1228    use std::ops::Range;
1229
1230    // An iterator that lies about its `ExactSizeIterator` implementation.
1231    // See https://github.com/PyO3/pyo3/issues/2118
1232    struct FaultyIter(Range<usize>, usize);
1233
1234    impl Iterator for FaultyIter {
1235        type Item = usize;
1236
1237        fn next(&mut self) -> Option<Self::Item> {
1238            self.0.next()
1239        }
1240    }
1241
1242    impl ExactSizeIterator for FaultyIter {
1243        fn len(&self) -> usize {
1244            self.1
1245        }
1246    }
1247
1248    #[test]
1249    #[should_panic(
1250        expected = "Attempted to create PyList but `elements` was larger than reported by its `ExactSizeIterator` implementation."
1251    )]
1252    fn too_long_iterator() {
1253        Python::with_gil(|py| {
1254            let iter = FaultyIter(0..usize::MAX, 73);
1255            let _list = PyList::new_bound(py, iter);
1256        })
1257    }
1258
1259    #[test]
1260    #[should_panic(
1261        expected = "Attempted to create PyList but `elements` was smaller than reported by its `ExactSizeIterator` implementation."
1262    )]
1263    fn too_short_iterator() {
1264        Python::with_gil(|py| {
1265            let iter = FaultyIter(0..35, 73);
1266            let _list = PyList::new_bound(py, iter);
1267        })
1268    }
1269
1270    #[test]
1271    #[should_panic(
1272        expected = "out of range integral type conversion attempted on `elements.len()`"
1273    )]
1274    fn overflowing_size() {
1275        Python::with_gil(|py| {
1276            let iter = FaultyIter(0..0, usize::MAX);
1277
1278            let _list = PyList::new_bound(py, iter);
1279        })
1280    }
1281
1282    #[cfg(feature = "macros")]
1283    #[test]
1284    fn bad_clone_mem_leaks() {
1285        use crate::{Py, PyAny};
1286        use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
1287        static NEEDS_DESTRUCTING_COUNT: AtomicUsize = AtomicUsize::new(0);
1288
1289        #[crate::pyclass]
1290        #[pyo3(crate = "crate")]
1291        struct Bad(usize);
1292
1293        impl Clone for Bad {
1294            fn clone(&self) -> Self {
1295                // This panic should not lead to a memory leak
1296                assert_ne!(self.0, 42);
1297                NEEDS_DESTRUCTING_COUNT.fetch_add(1, SeqCst);
1298
1299                Bad(self.0)
1300            }
1301        }
1302
1303        impl Drop for Bad {
1304            fn drop(&mut self) {
1305                NEEDS_DESTRUCTING_COUNT.fetch_sub(1, SeqCst);
1306            }
1307        }
1308
1309        impl ToPyObject for Bad {
1310            fn to_object(&self, py: Python<'_>) -> Py<PyAny> {
1311                self.to_owned().into_py(py)
1312            }
1313        }
1314
1315        struct FaultyIter(Range<usize>, usize);
1316
1317        impl Iterator for FaultyIter {
1318            type Item = Bad;
1319
1320            fn next(&mut self) -> Option<Self::Item> {
1321                self.0.next().map(|i| {
1322                    NEEDS_DESTRUCTING_COUNT.fetch_add(1, SeqCst);
1323                    Bad(i)
1324                })
1325            }
1326        }
1327
1328        impl ExactSizeIterator for FaultyIter {
1329            fn len(&self) -> usize {
1330                self.1
1331            }
1332        }
1333
1334        Python::with_gil(|py| {
1335            std::panic::catch_unwind(|| {
1336                let iter = FaultyIter(0..50, 50);
1337                let _list = PyList::new_bound(py, iter);
1338            })
1339            .unwrap_err();
1340        });
1341
1342        assert_eq!(
1343            NEEDS_DESTRUCTING_COUNT.load(SeqCst),
1344            0,
1345            "Some destructors did not run"
1346        );
1347    }
1348
1349    #[test]
1350    fn test_list_to_tuple() {
1351        Python::with_gil(|py| {
1352            let list = PyList::new_bound(py, vec![1, 2, 3]);
1353            let tuple = list.to_tuple();
1354            let tuple_expected = PyTuple::new_bound(py, vec![1, 2, 3]);
1355            assert!(tuple.eq(tuple_expected).unwrap());
1356        })
1357    }
1358}