You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust中Python子类重写含可变参数方法的借用冲突问题解决

问题分析与解决方案

错误根源

  1. PyCell借用冲突:在modify_object方法中,this.borrow_mut()持有Struct的可变引用,且作用域覆盖了call_method调用。而调用Python方法时会再次尝试借用PyCell<Struct>,违反了PyCell的借用规则:同一时间只能存在一个可变借用,或多个不可变借用,因此触发Already borrowed错误。
  2. 错误的对象转换:自定义的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 21:32:03