pyo3 icon indicating copy to clipboard operation
pyo3 copied to clipboard

Support IntoPyObject for Arc in pyclass for pyo3(get)

Open weixiao-huang opened this issue 10 months ago • 3 comments

I defined a class

#[pyclass(get_all)]
class MyClass {
  inner: Arc<Vec<String>>,
}

and I got the error below

`std::sync::Arc<std::vec::Vec<std::string::String>>` cannot be converted to a Python objec

weixiao-huang avatar Feb 02 '25 07:02 weixiao-huang

#4668 is somewhat related, but I think Arc may not have the same issues with semantics in Python that representing a rust atomic integer would?

ngoldbaum avatar Feb 05 '25 17:02 ngoldbaum

I think the question here is to define how to convert Arc<T>. We don't have access to a T by value so we can't just forward it that way. I guess it would make the most sense to forward to the implementation for &T using HRTB, but I don't know of a way to name it in the associated types. We could of course erase the types and just return PyAny and PyErr, but I'm not so sure whether we should do that.

impl<'py, T> IntoPyObject<'py> for Arc<T>
where
    for<'a> &'a T: IntoPyObject<'py>,
{
    type Target = <??>::Target;
    type Output = <??>::Output;
    type Error = <??>::Error;

    #[inline]
    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
        (&*self).into_pyobject(py)
    }
}

It is possible to implement on a reference, which would make it work for #[pyo3(get)] on #[pyclass], but would not work for #[derive(IntoPyObject)] (only #[derive(IntoPyObjectRef)]) or as return type.

impl<'a, 'py, T> IntoPyObject<'py> for &'a Arc<T>
where
    &'a T: IntoPyObject<'py>,
{
    type Target = <&'a T as IntoPyObject<'py>>::Target;
    type Output = <&'a T as IntoPyObject<'py>>::Output;
    type Error = <&'a T as IntoPyObject<'py>>::Error;

    #[inline]
    fn into_pyobject(self, py: crate::Python<'py>) -> Result<Self::Output, Self::Error> {
        (&**self).into_pyobject(py)
    }
}

I tend to think that writing a manual #[getter] for these cases is the better option.

Icxolu avatar Feb 08 '25 11:02 Icxolu

but I don't know of a way to name it in the associated types.

You can use additional type parameters. It is not pretty but it works 😅

impl<'py, A, T, O, E> IntoPyObject<'py> for std::sync::Arc<A>
where
        for<'a> &'a A: IntoPyObject<'py, Target = T, Output=O, Error = E>,
        O: BoundObject<'py, T>,
        E: Into<PyErr>
{
    type Target = T;
    type Output = O;
    type Error = E;

    #[inline]
    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
        (&*self).into_pyobject(py)
    }
}

bschoenmaeckers avatar Mar 14 '25 14:03 bschoenmaeckers