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

如何避免向Python函数传递父结构体时出现PyBorrowMutError

核心问题分析

你遇到的PyBorrowMutError本质是Rust与PyO3的借用规则冲突:当test_fn以不可变引用&self持有ParentFloat3实例时,PyO3会标记该实例处于不可变借用状态;此时Python函数调用update_at尝试获取可变引用,触发双重借用检查失败。仅使用Py<T>无法解决,因为&self的不可变借用标记仍未解除。

解决方案(unsafe方案,满足不复制/不改结构要求)

通过手动解除Rust端的不可变借用标记,让Python端可以安全获取可变引用。核心是利用PyO3的release_borrow方法绕过借用检查,同时严格控制unsafe的使用边界。

Rust代码修改(lib.rs)

use pyo3::{prelude::*, exceptions};

#[pyclass]
struct ParentFloat3 {
    data: [f32; 3],
}

#[pymethods]
impl ParentFloat3 {
    #[new]
    fn new() -> Self {
        ParentFloat3 { data: [0.0, 0.0, 0.0] }
    }

    fn test_fn(&self, py: Python, py_func: PyObject) -> PyResult<()> {
        // 将实例转换为Py<T>,用于传递给Python函数
        let py_self = self.into_py(py);

        // 手动解除Rust端的不可变借用标记
        // unsafe原因:必须确保当前存在对应不可变借用,且后续不再使用原&self
        unsafe {
            let obj_ptr = py_self.as_ptr();
            py.release_borrow(obj_ptr);
        }

        // 调用Python函数,此时实例无Rust端借用,Python可获取可变引用
        py_func.call1(py, (py_self,))?;

        // ⚠️ 禁止在此方法后续使用&self,否则会触发未定义行为
        Ok(())
    }

    fn update_at(&mut self, index: usize, value: f32) -> PyResult<()> {
        if index >= 3 {
            return Err(exceptions::PyIndexError::new_err("Index out of bounds (0-2)"));
        }
        self.data[index] = value;
        Ok(())
    }

    fn get_data(&self) -> [f32; 3] {
        self.data
    }
}

#[pymodule]
fn rust_pyo3_demo(_py: Python, m: &PyModule) -> PyResult<()> {
    m.add_class::<ParentFloat3>()?;
    Ok(())
}

Python测试代码(main.py)

from rust_pyo3_demo import ParentFloat3

def py_update(obj):
    obj.update_at(0, 10.0)
    print(f"Updated data: {obj.get_data()}")

if __name__ == "__main__":
    p = ParentFloat3()
    p.test_fn(py_update)

关键注意事项

  1. unsafe安全边界:
    • 调用release_borrow后,test_fn绝对不能再使用&self(任何读取/操作),否则会违反Rust借用规则,导致内存安全问题。
    • 依赖Python GIL的单线程执行特性,确保没有其他并发借用操作,此方案才是安全的。
  2. 为什么单独用Py<T>无效?
    仅传递Py<T>时,Rust端仍持有&self的不可变借用标记,PyO3的借用检查机制依然会阻止Python端获取可变引用,必须手动解除标记才能绕过限制。

内容的提问来源于stack exchange,提问作者Krupip

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 00:25:10