{"record":{"id":"33a1205f615275b2","repo":"PyO3/pyo3","slug":"set-iteration-should-be-infallible","errorCode":null,"errorMessage":"set iteration should be infallible","messagePattern":"set iteration should be infallible","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/types/set.rs","lineNumber":273,"sourceCode":"}\n\n/// PyO3 implementation of an iterator for a Python `set` object.\npub struct BoundSetIterator<'py>(Bound<'py, PyIterator>);\n\nimpl<'py> BoundSetIterator<'py> {\n    pub(super) fn new(set: Bound<'py, PySet>) -> Self {\n        Self(PyIterator::from_object(&set).expect(\"set should always be iterable\"))\n    }\n}\n\nimpl<'py> Iterator for BoundSetIterator<'py> {\n    type Item = Bound<'py, super::PyAny>;\n\n    /// Advances the iterator and returns the next value.\n    fn next(&mut self) -> Option<Self::Item> {\n        self.0\n            .next()\n            .map(|result| result.expect(\"set iteration should be infallible\"))\n    }\n\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        let len = ExactSizeIterator::len(self);\n        (len, Some(len))\n    }\n\n    #[inline]\n    fn count(self) -> usize\n    where\n        Self: Sized,\n    {\n        self.len()\n    }\n}\n\nimpl ExactSizeIterator for BoundSetIterator<'_> {\n    fn len(&self) -> usize {","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/PyO3/pyo3/blob/ac9b6899d348be4d54614d060dea53a645a12e36/src/types/set.rs#L255-L291","documentation":"The `Iterator::next` impl for a set's `BoundSetIterator` unwraps the inner `PyResult` with `.expect(\"set iteration should be infallible\")` because iterating a CPython set is guaranteed not to fail. If the underlying `PyIterator` returns an error, this panic fires, signaling interpreter-state corruption or misuse rather than a catchable library error.","triggerScenarios":"Calling `.next()` (any `for` loop or `.collect()` over `set.iter()` or `PySet` iteration) when the inner iterator yields `Err` — practically only after unsafe mutation/deallocation of the set during iteration or interpreter corruption.","commonSituations":"Mutating a set from other (native/callback) code while Rust iterates it, bad refcount handling in embedding code, alternate interpreters with different error behavior.","solutions":["Do not mutate the set (from Rust callbacks or re-entrant Python) while iterating it","Audit unsafe code holding raw pointers into the set during iteration","Iterate defensively via `__iter__`/`__next__` with `?` if interpreter state can't be trusted"],"exampleFix":"// before\nfor item in set.iter() { set.add(item)?; } // mutating during iteration\n// after\nlet items: Vec<_> = set.iter().map(|i| i.unbind()).collect();\nfor item in items { set.add(item)?; }","handlingStrategy":"try-catch","validationCode":"// snapshot before iterating to avoid mid-iteration mutation issues\nitems = list(py_set)","typeGuard":"fn is_set(obj: &Bound<'_, PyAny>) -> bool {\n    obj.downcast::<PySet>().is_ok()\n}","tryCatchPattern":"// fallible iteration pattern instead of the panicking wrapper\nlet it = set_obj.call_method0(\"__iter__\")?;\nloop {\n    match it.call_method0(\"__next__\") {\n        Ok(item) => { /* ... */ }\n        Err(e) if e.is_instance_of::<PyStopIteration>(py) => break,\n        Err(e) => return Err(e),\n    }\n}","preventionTips":["Never mutate a set while iterating it (copy first)","Avoid re-entrant Python calls that touch the set inside the loop","Audit unsafe code holding raw pointers during iteration"],"tags":["pyo3","panic","set","iterator"],"backgroundTag":"iterator-infallibility-panic","analyzedSha":"ac9b6899d348be4d54614d060dea53a645a12e36","analyzedAt":"2026-09-05T09:20:35.319Z","contentChangedAt":"2026-09-05T09:20:35.319Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}