Rust网格边分割函数如何避免可变与不可变同时借用?
问题
我编写了一个遍历网格所有边并分割长边的函数,但在尝试同时可变和不可变借用mesh结构体时出现编译错误。请问如何避免这种同时借用的问题?
函数代码:
fn split_edges(&self, mesh: &'a mut TMesh, max_edge_length: TMesh::ScalarType) { let edges: Vec<TMesh::EdgeDescriptor> = mesh.edges().collect(); for edge in edges { let edge_length = mesh.edge_length(edge); // Split long edges at the middle if edge_length > max_edge_length { let (v1, v2) = mesh.edge_positions(edge); let split_at = v1 + (v2 - v1).scale(cast(0.5).unwrap()); mesh.split_edge(edge, &split_at); } } }
编译错误:
error[E0502]: cannot borrow `*mesh` as mutable because it is also borrowed as immutable --> src\remeshing\incremental.rs:48:17 | 12 | impl<'a, TMesh: EditableMesh<'a>> IncrementalRemesher<'a, TMesh> { | -- lifetime `'a` defined here ... 39 | let edges: Vec<TMesh::EdgeDescriptor> = mesh.edges().collect(); | ------------ | | | immutable borrow occurs here | argument requires that `*mesh` is borrowed for `'a` ... 48 | mesh.split_edge(edge, &split_at); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
Trait定义:
pub trait Mesh<'a> { type ScalarType: Floating; type EdgeDescriptor: Clone + Copy; type VertexDescriptor: Clone + Copy; type FaceDescriptor: Clone + Copy; type FacesIter: Iterator<Item = Self::FaceDescriptor>; type VerticesIter: Iterator<Item = Self::VertexDescriptor>; type EdgesIter: Iterator<Item = Self::EdgeDescriptor>; /// Creates mesh from vertices and face indices fn from_vertices_and_indices(vertices: &Vec<Point3<Self::ScalarType>>, faces: &Vec<usize>) -> Self; /// Iterator over mesh faces fn faces(&'a self) -> Self::FacesIter; /// Iterator over mesh vertices fn vertices(&'a self) -> Self::VerticesIter; /// Iterator over mesh edges fn edges(&'a self) -> Self::EdgesIter; /// Returns positions of face vertices in ccw order fn face_positions(&self, face: Self::FaceDescriptor) -> (Point3<Self::ScalarType>, Point3<Self::ScalarType>, Point3<Self::ScalarType>); /// Returns face normal fn face_normal(&self, face: Self::FaceDescriptor) -> UnitVector3<Self::ScalarType>; /// Returns edge length fn edge_positions(&self, edge: Self::EdgeDescriptor) -> (Point3<Self::ScalarType>, Point3<Self::ScalarType>); /// Returns edge length fn edge_length(&self, edge: Self::EdgeDescriptor) -> Self::ScalarType; } /// /// Triangular mesh that supports editing operations /// pub trait EditableMesh<'a>: Mesh<'a> { fn collapse_edge(&mut self, edge: Self::EdgeDescriptor); fn is_edge_collapse_safe(&mut self, edge: Self::EdgeDescriptor) -> bool; fn flip_edge(&mut self, edge: Self::EdgeDescriptor); fn is_edge_flip_safe(&mut self, edge: Self::EdgeDescriptor) -> bool; fn split_edge(&mut self, edge: Self::EdgeDescriptor, at: &Point3<Self::ScalarType>); fn shift_vertex(&mut self, vertex: Self::VertexDescriptor, to: &Point3<Self::ScalarType>); }
解决方案
核心问题出在Mesh trait的生命周期绑定上:迭代器方法(faces/vertices/edges)被强制绑定到trait的'a生命周期,导致调用mesh.edges()时,mesh的不可变借用会持续整个'a周期(即函数全程),后续的可变借用自然会触发冲突。
1. 修改Mesh trait的生命周期设计
移除trait上的'a生命周期参数,让迭代器方法仅依赖调用时的临时借用(Rust会自动推断匿名生命周期'_)。这样调用edges()后,不可变借用会在collect()完成后立即释放,不会阻碍后续的可变操作。
修改后的Trait代码:
pub trait Mesh { type ScalarType: Floating; type EdgeDescriptor: Clone + Copy; type VertexDescriptor: Clone + Copy; type FaceDescriptor: Clone + Copy; type FacesIter: Iterator<Item = Self::FaceDescriptor>; type VerticesIter: Iterator<Item = Self::VertexDescriptor>; type EdgesIter: Iterator<Item = Self::EdgeDescriptor>; /// Creates mesh from vertices and face indices fn from_vertices_and_indices(vertices: &Vec<Point3<Self::ScalarType>>, faces: &Vec<usize>) -> Self; /// Iterator over mesh faces fn faces(&self) -> Self::FacesIter; /// Iterator over mesh vertices fn vertices(&self) -> Self::VerticesIter; /// Iterator over mesh edges fn edges(&self) -> Self::EdgesIter; /// Returns positions of face vertices in ccw order fn face_positions(&self, face: Self::FaceDescriptor) -> (Point3<Self::ScalarType>, Point3<Self::ScalarType>, Point3<Self::ScalarType>); /// Returns face normal fn face_normal(&self, face: Self::FaceDescriptor) -> UnitVector3<Self::ScalarType>; /// Returns edge length fn edge_positions(&self, edge: Self::EdgeDescriptor) -> (Point3<Self::ScalarType>, Point3<Self::ScalarType>); /// Returns edge length fn edge_length(&self, edge: Self::EdgeDescriptor) -> Self::ScalarType; } /// /// Triangular mesh that supports editing operations /// pub trait EditableMesh: Mesh { fn collapse_edge(&mut self, edge: Self::EdgeDescriptor); fn is_edge_collapse_safe(&mut self, edge: Self::EdgeDescriptor) -> bool; fn flip_edge(&mut self, edge: Self::EdgeDescriptor); fn is_edge_flip_safe(&mut self, edge: Self::EdgeDescriptor) -> bool; fn split_edge(&mut self, edge: Self::EdgeDescriptor, at: &Point3<Self::ScalarType>); fn shift_vertex(&mut self, vertex: Self::VertexDescriptor, to: &Point3<Self::ScalarType>); }
2. 调整实现的生命周期参数
对应地,IncrementalRemesher的实现也需要移除多余的'a生命周期绑定:
impl<TMesh: EditableMesh> IncrementalRemesher<TMesh> { fn split_edges(&self, mesh: &mut TMesh, max_edge_length: TMesh::ScalarType) { let edges: Vec<TMesh::EdgeDescriptor> = mesh.edges().collect(); for edge in edges { let edge_length = mesh.edge_length(edge); // Split long edges at the middle if edge_length > max_edge_length { let (v1, v2) = mesh.edge_positions(edge); let split_at = v1 + (v2 - v1).scale(cast(0.5).unwrap()); mesh.split_edge(edge, &split_at); } } } }
原理说明
原来的trait Mesh<'a>将迭代器与trait的生命周期强绑定,导致不可变借用的范围被过度延长。修改后,迭代器的生命周期仅依赖方法调用时的临时借用,collect()完成后借用立即失效,后续的split_edge可变借用就能正常执行,不再触发冲突。
内容的提问来源于stack exchange,提问作者Dima Volovyk
相关产品推荐
相关产品推荐

