pyo3/pyclass/
gc.rs

1use std::{
2    marker::PhantomData,
3    os::raw::{c_int, c_void},
4};
5
6use crate::{ffi, Py};
7
8/// Error returned by a `__traverse__` visitor implementation.
9#[repr(transparent)]
10pub struct PyTraverseError(NonZeroCInt);
11
12impl PyTraverseError {
13    /// Returns the error code.
14    pub(crate) fn into_inner(self) -> c_int {
15        self.0.into()
16    }
17}
18
19/// Object visitor for GC.
20#[derive(Clone)]
21pub struct PyVisit<'a> {
22    pub(crate) visit: ffi::visitproc,
23    pub(crate) arg: *mut c_void,
24    /// Prevents the `PyVisit` from outliving the `__traverse__` call.
25    pub(crate) _guard: PhantomData<&'a ()>,
26}
27
28impl PyVisit<'_> {
29    /// Visit `obj`.
30    ///
31    /// Note: `obj` accepts a variety of types, including
32    /// - `&Py<T>`
33    /// - `&Option<Py<T>>`
34    /// - `Option<&Py<T>>`
35    pub fn call<'a, T, U: 'a>(&self, obj: T) -> Result<(), PyTraverseError>
36    where
37        T: Into<Option<&'a Py<U>>>,
38    {
39        let ptr = obj.into().map_or_else(std::ptr::null_mut, Py::as_ptr);
40        if !ptr.is_null() {
41            match NonZeroCInt::new(unsafe { (self.visit)(ptr, self.arg) }) {
42                None => Ok(()),
43                Some(r) => Err(PyTraverseError(r)),
44            }
45        } else {
46            Ok(())
47        }
48    }
49}
50
51/// Workaround for `NonZero<c_int>` not being available until MSRV 1.79
52mod get_nonzero_c_int {
53    pub struct GetNonZeroCInt<const WIDTH: usize>();
54
55    pub trait NonZeroCIntType {
56        type Type;
57    }
58    impl NonZeroCIntType for GetNonZeroCInt<16> {
59        type Type = std::num::NonZeroI16;
60    }
61    impl NonZeroCIntType for GetNonZeroCInt<32> {
62        type Type = std::num::NonZeroI32;
63    }
64
65    pub type Type =
66        <GetNonZeroCInt<{ std::mem::size_of::<std::os::raw::c_int>() * 8 }> as NonZeroCIntType>::Type;
67}
68
69use get_nonzero_c_int::Type as NonZeroCInt;
70
71#[cfg(test)]
72mod tests {
73    use super::PyVisit;
74    use static_assertions::assert_not_impl_any;
75
76    #[test]
77    fn py_visit_not_send_sync() {
78        assert_not_impl_any!(PyVisit<'_>: Send, Sync);
79    }
80}
⚠️ Internal Docs ⚠️ Not Public API 👉 Official Docs Here