如何通过PyO3实现Rust Vec<>与Python的双向同步更新?
PyO3中Python端修改容器无法同步到Rust对象的解决方案
问题根源
当你在Mesh结构体的vertices字段上使用#[pyo3(get, set)]时,PyO3会自动生成getter方法:它会将Rust的Vec<MeshVertex>复制转换为Python的list(每个MeshVertex会被包装为Python对象,但整个list是新创建的副本)。因此,Python端对这个list的修改(比如append)仅作用于副本,不会同步到Rust原对象的Vec中。
解决方案:自定义可同步的顶点容器
通过创建一个自定义Python类MeshVertices,让它直接持有对Mesh对象的可变引用,实现Python序列操作方法来直接操作Rust端的Vec<MeshVertex>,从而实现双向同步。
1. 定义MeshVertices类
这个类作为RustVec<MeshVertex>的代理,所有操作都会直接映射到原Mesh的vertices字段:
use pyo3::prelude::*; #[pyclass] struct MeshVertices<'a> { mesh: PyRefMut<'a, Mesh>, } #[pymethods] impl<'a> MeshVertices<'a> { // 实现append方法,直接追加到原Rust Vec fn append(&mut self, v: MeshVertex) { self.mesh.vertices.push(v); } // 实现索引访问 fn __getitem__(&self, idx: usize) -> PyResult<MeshVertex> { self.mesh.vertices.get(idx) .cloned() .ok_or_else(|| PyErr::new::<pyo3::exceptions::PyIndexError, _>("Index out of bounds")) } // 实现长度获取 fn __len__(&self) -> usize { self.mesh.vertices.len() } // 实现迭代支持 fn __iter__(&self) -> PyResult<PyObject> { let iter = self.mesh.vertices.iter().cloned(); Python::with_gil(|py| Ok(iter.into_py(py))) } }
2. 修改Mesh结构体的getter
移除vertices字段上的自动getter注解,手动实现返回MeshVertices实例的getter:
#[pyclass] #[derive(Debug)] pub struct Mesh { pub vertices: Vec<MeshVertex>, #[pyo3(get, set)] pub facets: Vec<MeshFacet>, } #[pymethods] impl Mesh { #[new] fn new() -> Self { Mesh { facets: Vec::new(), vertices: Vec::new(), } } // 自定义vertices getter,返回代理容器 #[getter] fn vertices(&mut self) -> PyResult<MeshVertices> { let mesh_ref = PyRefMut::from_mut(self)?; Ok(MeshVertices { mesh: mesh_ref }) } // 保留原push_vertex方法(可选) fn push_vertex(&mut self, v: MeshVertex) { self.vertices.push(v); } }
3. Python端使用验证
现在Python端的append操作会直接同步到Rust的Mesh对象:
mv = plugin_api.MeshVertex() mesh.vertices.append(mv) # 此操作将同步更新Rust端的vertices print(len(mesh.vertices)) # 输出1,符合预期
扩展说明
- 若需要支持更多Python列表操作(如
pop、insert、remove等),只需在MeshVertices的#[pymethods]块中实现对应的方法,直接操作self.mesh.vertices即可。 MeshVertices持有PyRefMut<'a, Mesh>,遵循Rust的借用规则:在MeshVertices实例存在期间,原Mesh对象会被可变借用,无法进行其他可变操作;当MeshVertices被Python垃圾回收后,借用会自动释放。
内容的提问来源于stack exchange,提问作者Nikolay Shulga
相关产品推荐
相关产品推荐

