pyo3/pyclass/
gc.rs

1use std::{
2    marker::PhantomData,
3    num::NonZero,
4    os::raw::{c_int, c_void},
5};
6
7use crate::{ffi, Py};
8
9/// Error returned by a `__traverse__` visitor implementation.
10#[repr(transparent)]
11pub struct PyTraverseError(NonZero<c_int>);
12
13impl PyTraverseError {
14    /// Returns the error code.
15    pub(crate) fn into_inner(self) -> c_int {
16        self.0.into()
17    }
18}
19
20/// Object visitor for GC.
21#[derive(Clone)]
22pub struct PyVisit<'a> {
23    pub(crate) visit: ffi::visitproc,
24    pub(crate) arg: *mut c_void,
25    /// Prevents the `PyVisit` from outliving the `__traverse__` call.
26    pub(crate) _guard: PhantomData<&'a ()>,
27}
28
29impl PyVisit<'_> {
30    /// Visit `obj`.
31    ///
32    /// Note: `obj` accepts a variety of types, including
33    /// - `&Py<T>`
34    /// - `&Option<Py<T>>`
35    /// - `Option<&Py<T>>`
36    pub fn call<'a, T, U: 'a>(&self, obj: T) -> Result<(), PyTraverseError>
37    where
38        T: Into<Option<&'a Py<U>>>,
39    {
40        let ptr = obj.into().map_or_else(std::ptr::null_mut, Py::as_ptr);
41        if !ptr.is_null() {
42            match NonZero::new(unsafe { (self.visit)(ptr, self.arg) }) {
43                None => Ok(()),
44                Some(r) => Err(PyTraverseError(r)),
45            }
46        } else {
47            Ok(())
48        }
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use super::PyVisit;
55    use static_assertions::assert_not_impl_any;
56
57    #[test]
58    fn py_visit_not_send_sync() {
59        assert_not_impl_any!(PyVisit<'_>: Send, Sync);
60    }
61}