Struct with reference to own fields

Hello there, I have a simple struct which looks like this:

pub struct Graph<'a> {
    verts: Vec<Vertex>,
    edges: Vec<(&'a Vertex, &'a Vertex)>,
}

What I want is not exacly clear here: I want the tuple of Vertex References in Graph::edges to be references to items in verts.

When I try to create a Graph that way I get an error message, here’s an example:

fn example() -> Graph<'a> {
      let verts = vec![Vertex(0.0, 0.0, 0.0), Vertex(1.0, 1.0, 1.0)];
      let edges = vec![(&verts[0], &verts[1])];

      Graph { verts, edges }
}

wich results in the following error message:

E0515: cannot return value referencing local variable `verts` returns a value referencing data owned by the current function

and it makes sense, to some extend I get what the error message means, and my code obviously doesn’t clearly communicate it’s intended purposes.

The compiler really isn’t to blame for this but I’m quite glad I caught this error, for I have no idea how I’d solve it.

I am really looking forward to get better at rust thanks to this oppertunity, for now I’m stuck, could you help me out?

Thanks a lot for reading this far, I know my english can be far from good from time to time, really appreciate that you’ve made it through.

Ps: The E0515 message looks like this:

Summary

Cannot return value that references local variable

Local variables, function parameters and temporaries are all dropped before the
end of the function body. So a reference to them cannot be returned.

#![feature(nll)]
fn get_dangling_reference() -> &'static i32 {
    let x = 0;
    &x
}
#![feature(nll)]
use std::slice::Iter;
fn get_dangling_iterator<'a>() -> Iter<'a, i32> {
    let v = vec![1, 2, 3];
    v.iter()
}

Consider returning an owned value instead:

use std::vec::IntoIter;

fn get_integer() -> i32 {
    let x = 0;
    x
}

fn get_owned_iterator() -> IntoIter<i32> {
    let v = vec![1, 2, 3];
    v.into_iter()
}

The easiest thing here is to store your edges as usize indexes instead of references.

Thought about this as well and that most definitly seemed like the easiest way on first glance.
I’m kinda intrigued by the challenge of solving it this way though

It will be a severe challenge – Rust lifetimes are really not equipped to deal with self-referential structs. If you still want to explore this, rental or owning_ref might help.

2 Likes

To emphasize why this is hard, all of those references will be invalidated if you reallocate the Vec by inserting elements. So you will have to rely on unsafe code, either in your own code or a crate (like rental or owning_ref). You will likely also be unable to modify the Node unless you use interior mutability because &T gaurentees that T won’t change in the absense of interior mutability. The index solutions does not suffer from this problems.

2 Likes

…though indices can be invalidated if elements are deleted from the Vec.

1 Like

Yes, but that isn’t unsafe, just a logic bug.

1 Like

These are really interesting reads, thank you!

Brilliant, thank you!
I didn’t thought about that, now I can see the problem from a whole new perspective. Glad you mentioned that