I have two structs: Struct and InnerStruct. Struct has two methods: modify_object
that uses modify_object_inner
. Rust implementation of modify_object_inner
doesn't matter because I want to implement this method in class that will inherit after my Struct in Python. Function modify_object
modifies field of Struct of type InnerStruct. I made this code and it compiles:
use pyo3::prelude::*;
use pyo3::types::PyDict;
#[pyclass(subclass)]
#[derive(Clone)]
pub struct InnerStruct {
#[pyo3(get,set)]
pub field: i32
}
#[pyclass(subclass)]
pub struct Struct {
#[pyo3(get,set)]
pub inner_struct: InnerStruct
}
#[pymethods]
impl InnerStruct {
#[new]
fn new(field: i32) -> Self {
InnerStruct {field}
}
}
// I had to implement this because of error "error[E0277]: the trait bound `&mut InnerStruct: ToPyObject` is not satisfied"
impl ToPyObject for &mut InnerStruct {
fn to_object(&self, py: Python<'_>) -> PyObject {
let dict = PyDict::new(py);
dict.set_item("field", self.field).expect("Failed to set field in dictionary");
dict.into()
}
}
#[pymethods]
impl Struct {
#[new]
fn new(inner_struct: InnerStruct) -> Self {
Struct { inner_struct}
}
fn modify_object(this: &PyCell<Self>) -> () {
Python::with_gil(|py| {
let inner_struct = &mut this.borrow_mut().inner_struct;
let kwargs = PyDict::new(py);
kwargs.set_item("object_to_modify", inner_struct).expect("Error with set_item");
this.call_method("modify_object_inner", (), Some(kwargs)).expect("Error with call_method");
});
}
fn modify_object_inner(&mut self, object_to_modify: &mut InnerStruct) {
object_to_modify.field = -1
}
}
#[pymodule]
fn my_rust_module(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<Struct>()?;
m.add_class::<InnerStruct>()?;
Ok(())
}
But when I tested it with this Python code:
from my_rust_module import InnerStruct, Struct
import os
os.environ['RUST_BACKTRACE'] = '1'
class PythonStruct(Struct):
def __new__(cls, inner_struct):
return super().__new__(cls, inner_struct)
inner_struct = InnerStruct(0)
ps = PythonStruct(inner_struct)
ps.modify_object()
print(ps.inner_struct.field) # without overwriting should print -1
class PythonListElement(Struct):
def __new__(cls, inner_struct):
return super().__new__(cls, inner_struct)
def modify_object_inner(self, inner_struct):
inner_struct.field = 1
inner_struct = InnerStruct(0)
ps = PythonStruct(inner_struct)
ps.modify_object()
print(ps.inner_struct.field) # without overwriting should print 1
I got:
thread '<unnamed>' panicked at 'Error with call_method: PyErr { type: <class 'RuntimeError'>, value: RuntimeError('Already borrowed'), traceback: None }', src\lib.rs:46:71
If someone knows the answer, please post additionally a source of your knowledge (for example link to appropriate part of doc), because I'm quite lost and don't know how to find answers myself.