{"record":{"id":"28499aa8041af166","repo":"PyO3/pyo3","slug":"set-should-always-be-iterable","errorCode":null,"errorMessage":"set should always be iterable","messagePattern":"set should always be iterable","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/types/set.rs","lineNumber":262,"sourceCode":"    type Item = Bound<'py, PyAny>;\n    type IntoIter = BoundSetIterator<'py>;\n\n    /// Returns an iterator of values in this set.\n    ///\n    /// # Panics\n    ///\n    /// If PyO3 detects that the set is mutated during iteration, it will panic.\n    fn into_iter(self) -> Self::IntoIter {\n        self.iter()\n    }\n}\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","sourceCodeStart":244,"sourceCodeEnd":280,"githubUrl":"https://github.com/PyO3/pyo3/blob/ac9b6899d348be4d54614d060dea53a645a12e36/src/types/set.rs#L244-L280","documentation":"`BoundSetIterator::new` converts a `PySet` into a `PyIterator` and asserts this always succeeds because Python sets are contractually iterable. The panic fires only if `PyIterator::from_object` fails, i.e. the object reaching this code is not actually iterable — an internal invariant breach, usually meaning a non-set object was downcast into `PySet` unsafely.","triggerScenarios":"Constructing a set iterator from a `Bound<'py, PySet>` that was produced by an unsafe/incorrect cast (e.g. `cast_into_unchecked` on an object that is not a set), typically under RustPython/GraalPy or misbehaving FFI code.","commonSituations":"Unsafe downcasts of arbitrary Python objects to `PySet`, embedding alternate interpreters where type checks differ, or corrupted objects from bad FFI refcount handling.","solutions":["Avoid unchecked casts; use `PySet::extract`/`downcast` (checked) before iterating","If you cannot guarantee the type, iterate via `PyIterator::from_object` and handle the error yourself","Verify the interpreter build if using RustPython/GraalPy"],"exampleFix":"// before\nlet set: &Bound<PySet> = obj.cast_into_unchecked();\nfor x in set.iter() { ... }\n// after\nlet set: &Bound<PySet> = obj.downcast()?;\nfor x in set.iter() { ... }","handlingStrategy":"type-guard","validationCode":"if not isinstance(obj, set):\n    raise TypeError(\"expected a set\")","typeGuard":"fn as_set<'py>(obj: &Bound<'py, PyAny>) -> PyResult<&Bound<'py, PySet>> {\n    obj.downcast::<PySet>()\n}","tryCatchPattern":"// checked conversion before the panicking iterator\nlet set = obj.downcast::<PySet>()?;\nfor item in set.iter() { /* ... */ }","preventionTips":["Avoid cast_into_unchecked on untrusted objects","Only obtain Bound<PySet> via extract/downcast","Validate interpreter behavior if embedding RustPython/GraalPy"],"tags":["pyo3","panic","set","iterator"],"backgroundTag":"set-iterability-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"}