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

如何解决Rust中同一对象无法拥有两个可变引用的借用检查问题?

问题

我正在移植Constructive Solid Geometry (CSG)库来学习Rust,目前被借用检查器的「同一对象不能拥有两个可变引用」规则卡住了。我实现了一个网格切割函数,类Rust伪代码如下:

fn cut(
    mesh: &Vec<Polygon>,
    cutting_plane: &Plane,
    front: &mut Vec<Polygon>,
    back: &mut Vec<Polygon>,
    coplanar_front: &mut Vec<Polygon>,
    coplanar_back: &mut Vec<Polygon>)
{
    for polygon in mesh.iter() {
        let polygon_position = calculate_polygon_position_in_relation_to_plane(&polygon, &cutting_plane);

        match polygon_position {
            InFront => {
                front.push(polygon.clone());
            }

            InBack => {
                back.push(polygon.clone());
            }

            Coplanar => {
                if polygon is coplanar with cutting_plane but still a little bit in the front {
                    coplanar_front.push(polygon.clone());
                } else {
                    coplanar_back.push(polygon.clone());
                }
            }

            Spanning => {
                let (front_polygon, back_polygon) = cut_polygon(&polygon, &cutting_plane);
                front.push(front_polygon);
                back.push(back_polygon);
            }
        }
    }
}

这个函数的结果依赖传入的4个Vec参数(front、back、coplanar_front、coplanar_back)的填充情况。我需要两种调用方式:

第一种方式:关心共面多边形的具体位置

/* ... 初始化 some_mesh 和 some_cutting_plane ... */

let mut front = Vec::new();
let mut back = Vec::new();
let mut coplanar_front = Vec::new();
let mut coplanar_back = Vec::new();

cut(&some_mesh, &some_cutting_plane, &mut front, &mut back, &mut coplanar_front, &mut coplanar_back);

/* ... 使用 coplanar_front、coplanar_back、front 和 back 向量 */

第二种方式:不关心共面多边形的具体位置,想把共面的都放到同一个Vec里,但借用检查器会阻止这种调用,因为同一对象不能有两个可变引用

/* ... 初始化 some_mesh 和 some_cutting_plane ... */

let mut front = Vec::new();
let mut back = Vec::new();
let mut coplanar = Vec::new();

// 这里会被借用检查器阻止
cut(&some_mesh, &some_cutting_plane, &mut front, &mut back, &mut coplanar, &mut coplanar);

/* ... 使用 coplanar、front 和 back 向量 */

我不想用创建临时数组再合并的朴素方案(会产生不必要的拷贝),也不想写两个大部分代码重复的函数。我知道Box这类结构可以用于多可变引用,但不知道正确用法。请问Rust里有没有合适的特性或更优的解决思路?

解决思路

方法1:使用&mut dyn Extend<Polygon>参数实现灵活扩展

将coplanar_front和coplanar_back的参数类型从&mut Vec<Polygon>改为&mut dyn Extend<Polygon>,这样可以接收任何支持扩展元素的类型,包括原生Vec,或者自定义的合并适配器,完美兼容两种调用场景。

首先修改cut函数的签名与内部逻辑:

fn cut(
    mesh: &[Polygon], // 改用&[Polygon]比&Vec<Polygon>更通用
    cutting_plane: &Plane,
    front: &mut Vec<Polygon>,
    back: &mut Vec<Polygon>,
    coplanar_front: &mut dyn Extend<Polygon>,
    coplanar_back: &mut dyn Extend<Polygon>,
) {
    for polygon in mesh {
        let polygon_position = calculate_polygon_position_in_relation_to_plane(polygon, cutting_plane);

        match polygon_position {
            InFront => front.push(polygon.clone()),
            InBack => back.push(polygon.clone()),
            Coplanar => {
                if polygon_is_coplanar_front(polygon, cutting_plane) {
                    coplanar_front.extend_one(polygon.clone());
                } else {
                    coplanar_back.extend_one(polygon.clone());
                }
            }
            Spanning => {
                let (front_poly, back_poly) = cut_polygon(polygon, cutting_plane);
                front.push(front_poly);
                back.push(back_poly);
            }
        }
    }
}

然后定义一个简单的适配器,用于将两个分支的共面多边形合并到同一个Vec:

struct MergeExtend<'a>(&'a mut Vec<Polygon>);

impl<'a> Extend<Polygon> for MergeExtend<'a> {
    fn extend_one(&mut self, item: Polygon) {
        self.0.push(item);
    }

    fn extend<T: IntoIterator<Item = Polygon>>(&mut self, iter: T) {
        self.0.extend(iter);
    }
}

现在两种调用方式都能正常工作:

// 场景1:分开存储共面多边形
let mut front = Vec::new();
let mut back = Vec::new();
let mut coplanar_front = Vec::new();
let mut coplanar_back = Vec::new();
cut(&some_mesh, &some_cutting_plane, &mut front, &mut back, &mut coplanar_front, &mut coplanar_back);
// 场景2:合并共面多边形到同一个Vec
let mut front = Vec::new();
let mut back = Vec::new();
let mut coplanar = Vec::new();
cut(
    &some_mesh,
    &some_cutting_plane,
    &mut front,
    &mut back,
    &mut MergeExtend(&mut coplanar),
    &mut MergeExtend(&mut coplanar),
);

这种方案无额外拷贝,也不需要重复编写函数逻辑,是最优雅的解决方式。

方法2:使用RefCell绕开编译期借用检查

如果不想大幅修改函数签名,可以用RefCell将编译期的借用检查延迟到运行时。由于你的场景中,coplanar_front和coplanar_back的写入分支是互斥的(同一个多边形只会进入其中一个分支),因此不会触发运行时panic。

调用代码示例:

use std::cell::RefCell;

let mut front = Vec::new();
let mut back = Vec::new();
let coplanar = RefCell::new(Vec::new());

cut(
    &some_mesh,
    &some_cutting_plane,
    &mut front,
    &mut back,
    &mut coplanar.borrow_mut(),
    &mut coplanar.borrow_mut(),
);

这种方式有轻微的运行时开销,且依赖业务逻辑的互斥保证,适合快速适配现有代码的场景。

方法3:重构为返回自定义结果结构体

将cut函数改为返回包含所有结果的结构体,再提供合并共面多边形的方法,兼顾两种使用场景:

struct CutResult {
    front: Vec<Polygon>,
    back: Vec<Polygon>,
    coplanar_front: Vec<Polygon>,
    coplanar_back: Vec<Polygon>,
}

impl CutResult {
    // 合并共面多边形的方法
    fn merged_coplanar(self) -> (Vec<Polygon>, Vec<Polygon>, Vec<Polygon>) {
        let mut coplanar = self.coplanar_front;
        coplanar.extend(self.coplanar_back);
        (self.front, self.back, coplanar)
    }
}

fn cut(mesh: &[Polygon], cutting_plane: &Plane) -> CutResult {
    let mut front = Vec::new();
    let mut back = Vec::new();
    let mut coplanar_front = Vec::new();
    let mut coplanar_back = Vec::new();

    // 原函数的填充逻辑

    CutResult {
        front,
        back,
        coplanar_front,
        coplanar_back,
    }
}

调用方式:

// 场景1:使用分开的共面多边形
let result = cut(&some_mesh, &some_cutting_plane);
// 直接使用 result.front、result.back、result.coplanar_front、result.coplanar_back
// 场景2:使用合并后的共面多边形
let (front, back, coplanar) = cut(&some_mesh, &some_cutting_plane).merged_coplanar();

这种方案的extend操作开销极低(Vec会直接移动内存块而非逐元素拷贝),且代码结构更清晰,适合追求可读性的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 03:57:06