如何避免向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)
关键注意事项
- unsafe安全边界:
- 调用
release_borrow后,test_fn绝对不能再使用&self(任何读取/操作),否则会违反Rust借用规则,导致内存安全问题。 - 依赖Python GIL的单线程执行特性,确保没有其他并发借用操作,此方案才是安全的。
- 调用
- 为什么单独用
Py<T>无效?
仅传递Py<T>时,Rust端仍持有&self的不可变借用标记,PyO3的借用检查机制依然会阻止Python端获取可变引用,必须手动解除标记才能绕过限制。
内容的提问来源于stack exchange,提问作者Krupip
相关产品推荐
相关产品推荐

