Rust中Python子类重写含可变参数方法的借用冲突问题解决
问题分析与解决方案
错误根源
- PyCell借用冲突:在
modify_object方法中,this.borrow_mut()持有Struct的可变引用,且作用域覆盖了call_method调用。而调用Python方法时会再次尝试借用PyCell<Struct>,违反了PyCell的借用规则:同一时间只能存在一个可变借用,或多个不可变借用,因此触发Already borrowed错误。 - 错误的对象转换:自定义的
ToPyObject for &mut InnerStruct实现将对象转换为字典传递,导致Python方法修改的是字典副本而非原InnerStruct实例,无法实现预期的修改效果。
修正方案
1. 调整结构体字段类型
将Struct中的inner_struct字段改为Py<InnerStruct>类型,确保可以在Rust与Python之间传递对象引用,而非克隆或字典。
2. 重构Rust实现代码
use pyo3::prelude::*; #[pyclass(subclass)] #[derive(Clone)] pub struct InnerStruct { #[pyo3(get, set)] pub field: i32, } #[pyclass(subclass)] pub struct Struct { #[pyo3(get, set)] pub inner_struct: Py<InnerStruct>, } #[pymethods] impl InnerStruct { #[new] fn new(field: i32) -> Self { InnerStruct { field } } } #[pymethods] impl Struct { #[new] fn new(inner_struct: Py<InnerStruct>) -> Self { Struct { inner_struct } } fn modify_object(this: &PyCell<Self>) -> PyResult<()> { let py = this.py(); // 不可变借用获取对象引用,作用域结束后自动释放 let inner_struct = this.borrow().inner_struct.clone(); // 已释放Struct的借用,调用方法无冲突 this.call_method(py, "modify_object_inner", (inner_struct,), None)?; Ok(()) } fn modify_object_inner(&mut self, py: Python<'_>, inner_struct: &PyCell<InnerStruct>) -> PyResult<()> { let mut inner = inner_struct.borrow_mut(); inner.field = -1; Ok(()) } } #[pymodule] fn my_rust_module(_py: Python, m: &PyModule) -> PyResult<()> { m.add_class::<Struct>()?; m.add_class::<InnerStruct>()?; Ok(()) }
3. 修正Python测试代码
from my_rust_module import InnerStruct, Struct import os os.environ['RUST_BACKTRACE'] = '1' # 测试默认实现 inner_struct = InnerStruct(0) ps = Struct(inner_struct) ps.modify_object() print(ps.inner_struct.field) # 输出 -1 # 测试重写方法 class PythonStruct(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) # 输出 1
核心知识点
- PyCell借用规则:PyO3的
PyCell严格遵循Rust的借用语义,长时间持有可变借用会阻塞后续对对象的访问。 - Py
类型 :Py<T>是PyO3中用于持有Python对象引用的安全类型,可在Rust与Python之间传递对象,保持引用关系。 - 自动trait实现:对于
#[pyclass]标记的类型,PyO3会自动实现ToPyObject等转换trait,无需自定义实现。
内容的提问来源于stack exchange,提问作者rustbegginer
相关产品推荐
相关产品推荐

