Borrow multiple times and life time

Hi guys I am very new to rust (2 days so far) and I think I don't understand the borrowing and lifecycle right.
I want to have "global" vectors into which struct methods are able to push.
This is what I have so far:

#[derive(Debug)]
struct Vector {
    x: f64,
    y: f64,
}

impl Vector {
    fn new(x: f64, y: f64) -> Self {
        Self {
            x: x,
            y: y,
        }
    }
}

#[derive(Debug)]
struct Edge<'a> {
    vertices: [&'a Vector; 2],
}

impl<'a> Edge<'a> {
    pub fn new(vertices: &'a mut Vec<Vector>, x_a: f64, y_a: f64, x_b: f64, y_b: f64) -> Self {
        vertices.push(Vector::new(x_a, y_a));
        vertices.push(Vector::new(x_b, y_b));
        Self {
            vertices: [
                &vertices[vertices.len() - 2],
                &vertices[vertices.len() - 1],
            ]
        }
    }
}

#[derive(Debug)]
struct Face<'a> {
    edges: [&'a Edge<'a>; 4],
}

impl<'a> Face<'a> {
    pub fn new(edges: &'a mut Vec<Edge<'a>>, vertices: &'a mut Vec<Vector>) -> Self {
        for _ in 0..4 {
            edges.push(Edge::new(vertices, 1.0, 2.0, 1.0, 3.0));
        }
        Self {
            edges: [
                &edges[edges.len() - 4],
                &edges[edges.len() - 3],
                &edges[edges.len() - 2],
                &edges[edges.len() - 1],
            ]
        }
    }
}

fn main() {
    let mut vertices: Vec<Vector> = Vec::new();
    let mut edges: Vec<Edge> = Vec::new();
    let face = Face::new(&mut edges, &mut vertices);
    println!("{:?}", face);
}

The error reads like this:

cannot borrow `*vertices` as mutable more than once at a time
`*vertices` was mutably borrowed here in the previous iteration of the looprustcE0499
main.rs(39, 6): lifetime `'a` defined here
main.rs(42, 24): argument requires that `*vertices` is borrowed for `'a`
|
39 | impl<'a> Face<'a> {
   |      -- lifetime `'a` defined here
...
42 |             edges.push(Edge::new(vertices, 1.0, 2.0, 1.0, 3.0));
   |                        ----------^^^^^^^^---------------------
   |                        |         |
   |                        |         `*vertices` was mutably borrowed here in the previous iteration of the loop
   |                        argument requires that `*vertices` is borrowed for `'a`

I think this kind of question is similar to a lot of other questions in this forum, but having an answer depending on my own code would be nice. I tried some wired stuff with RefCell too, but I dont really understand the concept.

Thank you very much!

In general, early on when learning Rust, you should try hard to avoid defining structs with lifetime parameters like struct Edge<'a> or struct Face<'a>, as they likely don’t do what you’re trying to achieve, and working with these is a quick path into borrow-checking-and-lifetime hell. Borrowing in Rust is most usable for short-timed interactions; not holding onto references for too long makes your life easier, not putting them into other data structures helps with that.

On a similar note, (function arguments with) types of the form &'a mut SomeType where SomeType itself (syntactically) also uses the same lifetime 'a, such as your &'a mut Vec<Edge<'a>>, are a bad antipattern as they almost immediately lead to cannot borrow … as mutable more than once at a time or similar errors if you try to use them.

Consider working with owned data, like e.g.

struct Edge {
    vertices: [Vector; 2],
}

and

struct Face {
    edges: [Edge; 4],
}

Your <'a> arguments on the structs probably come from the attempt to put reference types &T into struct fields, then following compiler suggestions. If you want/need to have indirection for some reason, you could use Box<T>. Compiler suggestions on lifetime annotations aren’t always “right”, they can mislead you, taking your code further from what you actually mean, see points 5 and 7 in this list of “Lifetime Misconceptions” generally worth reading.

1 Like

If you really want to follow this design, you might want to elaborate more on why this approach would be useful. What’s to be achieved? There’s multiple approaches I can think of of how to change your code into something Rust accepts, depending on your goals. One is to replace the &Vector and &Edge by simple usize indices into those “global” vectors, and having most/all methods of Vector/Edge/Face take (potentially mutable) references to the required vectors as additional arguments.

Code example for an approach using indices: click here. (Feel free to ignore the code for Debug pretty printing. Notably, that code even does use structs with lifetime parameters after all, but these structs are very short-lived when they're used, so it doesn't create any problems.)

2 Likes

Wow thank you very much.

Your <'a> arguments on the structs probably come from the attempt to put reference types &T into struct fields,

Exactly what happend. Thanks a alot for your recommendations. I will read about Box now. And your links are gold! Didn't found thoose and they describe exactly what I did =)

The reason for having "global vectors" for edges and vertices is that some faces will share edges with other faces. Later I want to iterate all vertices without going through all faces->edges too. I want to create a patterns with many faces and ofc I like to have my code structured. Structs were obvious to me.
What do you think about that approach? Do you have a better idea?

Now I am going to look at your example. The usize indices will do the trick, I guess. Since I'll never delete faces or edges that might actually be the way I go.

Thank you so so much! Your answers helped me lot and I can use them to start digging even more into this beautiful language!

Splitting the data from the structs and having the struct storing to the index of the vector where the data lives in is brilliant! It is like an intermediate struct just pointing to the structs data. Very nice!

My next step is to put faces into a pattern struct and I can follow your approach =)

The debug formatter is nice too! It would have took me ages to come up with that. Thanks for that too!

One little question I have on how you get the index out of that tuple struct. For example in DebugEdge you have that line:

let data = &self.1[self.0 .0];

Why you don't write it like this:

let data = &self.1[self.0.0];

Why keeping the space between? I can't find anything simelar in the docs.
I see there is a diffrene cause my VS Code says so =)
Without the space it says:

let data: &<Vec<EdgeData, Global> as Index<&Edge>>::Output

With space:

let data: &EdgeData

Thanks a lot for your example code! Really really nice!

I just applied rustfmt and that added the space. I suppose it's done to make it more clear that the 0.0 is not a number with a decimal. I don't believe it makes a difference because there is special handling in the parser that makes .0.0 without the space work as well.

Edit: Ah, look, the support for .0.0 without a space is still fairly new

Nice link!
That makes sense. Thank you again you helped me alot!

With the help of your example I managed to put everything into a pattern struct:
here

Thank you!

The DebugXyz helper structs were just for being able to temporarily bundle extra data with the values so that a Debug implementation can be written. For Pattern you don’t really need that anymore, a simple

impl fmt::Debug for Pattern {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Pattern")
            .field(
                "faces",
                &DebugFaces(&self.faces, &self.edges, &self.vertices),
            )
            .finish()
    }
}

will do.

Ah yes good point! I actually kind of noticed it but didn't thought further...
Thanks again

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.