Search code examples
rustpyo3

How to implement trait FromPyObject for my rust struct


Consider a simple rust class exposed via pyo3 to python

use pyo3::prelude::*;

#[pyclass(name=MyClass)]
pub struct PyMyClass {
  // Some fields
}

#[pymethods]
impl PyMyStruct {
  #[new]
  fn py_new(obj: PyRawObject) {
    obj.init({
      PyMyStruct {
        // ...
      }
    });
  }
}

now there is a function which should be called with two of this structs from python in that manner:

a = MyStruct()
b = MyStruct()

c = foo(a,b)

Therefore one defines

#[pyfunction]
fn foo(a: PyMyStruct, b: PyMyStruct) -> PyResult<PyMyStruct> {
  // some high performance logic implemented in rust ...
}

Now the compiler claims PyMyStruct should implement the trait FromPyObject:

impl FromPyObject<'_> for PyMyStruct {
    fn extract(ob: &'_ PyAny) ->PyResult<Self> {
        // I dont know what to do here :(
    }
}

But I dont know how to retrieve an instance, pointer or whatever of PyMyStruct from PyAny... Can someone help me?


Solution

  • I don't know much about this crate but I think that you should take your objects by reference. Note that they are shared with the rest of the Python interpreter, so you can't actually own them.

    That is, just write:

    #[pyfunction]
    fn foo(a: &PyMyStruct, b: &PyMyStruct) -> PyResult<PyMyStruct> {
      // some high performance logic implemented in rust ...
    }
    

    Or if you want mutable objects:

    #[pyfunction]
    fn foo(a: &mut PyMyStruct, b: &mut PyMyStruct) -> PyResult<PyMyStruct> {
      // some high performance logic implemented in rust ...
    }
    

    This works because there are these impls:

    impl<'a, T> FromPyObject<'a> for &'a T where
        T: PyTryFrom<'a>
    impl<'a, T> FromPyObject<'a> for &'a mut T where
        T: PyTryFrom<'a>,
    

    and then this other one:

    impl<'v, T> PyTryFrom<'v> for T where
        T: PyTypeInfo
    

    What happens if you use the &mut variant and you pass the same object twice to this function? Well, I'm not sure but skimming the code I guess that you get two &mut references to the same object, and thus undefined behavior. If I were you, I'd use the non mut variant and use a RefCell if you actually want to change the object.