Building a referencing struct from temporaries

Let's say I have the following struct:

struct Polygon<'a>(Vec<&'a Segment<'a>);

And 2 ways of instanciating it:

fn new(segments: Vec<&'a Segment>) -> Self {
    /* Performs some checks */

    Self(segments)
}

fn from_points(points: Vec<&'a Point>) -> Self {
    /* Builds segments from points */

    Self::new(segments)
}

In from_point, since I'm building the segments inside the constructor, I have to do heap allocation using a combination of Box::new and Box::leak before passing those references to new. Otherwise the temporaries are dropped at the end of the block while still borrowed.

Now, the problem is that I have to manually drop those segments when the polygon instance is dropped to avoid memory leaks.

What's the best way to achieve that ?

I came up with 2 solutions so far:

  1. Replacing the struct by an enum
enum Polygon<'a> {
    Polygon(Vec<&'a Segment<'a>),
    OwnedPolygon(Vec<&'a Segment<'a>),
}

Then implementing a custom destructor for OwnedPolygon instances.

Not ideal because this induces a lot of useless branching at runtime.

  1. Introducing a const generic parameter
struct Polygon<'a, const DROP: bool>(Vec<&'a Segment<'a>);

Then implementing a custom destructor for Polygon<'a, true>.

Not ideal because if I have to handle collections of polygons the DROP parameter would make them heterogeneous, which means I'd have to resort to dynamic dispatch.

Sounds like it would be best to change Segment to be an owned data structure rather than having lifetimes. And probably also Polygon for that matter.

Do not try to manually deallocate your leaked boxes. Not a good idea.

3 Likes

The approach to things like this in the standard library is to have separate owned and borrowed versions of a type (e.g. String and &str), then have the owned version implement Deref. That may be a little tricky in your case though, since you have a vector of references, so you would need to collect into a vector to get the borrowed version. Do you really need it to work with borrowed Segments? If you need a mix of owned and borrowed Segments, then having Polygon store Segments using Cow may work. But if it's not strictly necessary and Segment isn't too complex, it may be best to work with owned Segments. It might be helpful if we could see the definition of Segment to determine whether it needs to be borrowed.

You may be interested in:

2 Likes

Amazing!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.