1

I am currently in process of learning Rust by porting a Constructive Solid Geometry (CSG) library and I have been wrestling with borrow checker and its' "no 2 mutable references on single object" policy. In the CSG library, I have a function for cutting mesh, which in rust-like pseudo-code looks something like this:

    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);
                }
            }
        }
    }

The result of this function then depends on how the function will fill the 4 vectors (front, back, coplanar_front, coplanar_back) passed in the arguments. The problem is, that I need to call the function both like this:

    /* ... setup some_mesh and some_cutting_plane ... */

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

    //I care about where the coplanar polygons precisely lie
    cut(&some_mesh, &some_cutting_plane, &mut front, &mut back, &mut coplanar_front, &mut coplanar_back);

    /* ... use coplanar_front, coplanar_back, front and back vectors */

and like this:

    /* ... setup some_mesh and some_cutting_plane ... */

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

    //I don't care where the coplanar polygons actually lie
    cut(&some_mesh, &some_cutting_plane, &mut front, &mut back, &mut coplanar, &mut coplanar);

    /* ... use coplanar, front and back vectors ... */

Where obviously in the second case, the borrow checker will stop me, since borrowing 2 mutable references for the same value is a big no no.

The naive solution in the second case, is to create a temporary array for one co-planar half and then merge it with the other, however this also comes with a price of a needless array copy.

I also don't want to create 2 versions of the same function, where one would work with just 1 co-planar vector and the other with 2 co-planar vectors, since most of the code would be the same.

I know that there are structures like Box, that allow borrowing multiple mutable references, but I have been unable to fully grasp the concept of how to use it properly, because I always stumble back to the original problem, where I need 2 mutable references on the same object.

Is there something Rust can do I am not aware of, or is there a better way to approach this problem?

Akufishi
  • 11
  • 2

1 Answers1

0

You could take an Option<&mut Vec<Polygon>> as second coplanar parameter and push to the first if it's None:

fn cut(
    mesh: &Vec<Polygon>,
    cutting_plane: &Plane,
    front: &mut Vec<Polygon>,
    back: &mut Vec<Polygon>,
    coplanar_front: &mut Vec<Polygon>,
    mut coplanar_back: Option<&mut Vec<Polygon>>,
) {
    //…
        Coplanar => {
            // instead of the if:
            match (
                // polygon is coplanar with cutting_plane but still
                // a little bit in the front
                true,
                // we have to make `coplanar_back` mutable in the arguments
                // list to call `as_mut` here, we have to call `as_mut` because
                // mutable references are not `Copy` and thus we would move
                // `coplanar_back`
                coplanar_back.as_mut(),
            ) {
                (false, Some(coplanar_back)) => coplanar_back.push(polygon.clone()),
                _ => coplanar_front.push(polygon.clone()),
            }
        }
    //…
}
cafce25
  • 15,907
  • 4
  • 25
  • 31