Struct field referring to another same struct field

#1

I’m currently building a graphics engine using Vulkan. The structure is basically the following:

 // Abstraction layer around the GPU
struct Gpu {
    // some fields
}
// Use the GPU to store and display objects
struct World<'a> {
    gpu: &'a Gpu,
    // some fields
}

My problem is that I would like to encapsulate both Gpu and World in the same structure:

struct GraphicsEngine<'a> {
    gpu: Gpu,
    world: World<'a>
}

fn new_graphics_engine<'a>() -> GraphicsEngine<'a> {
    let gpu = Gpu { ... };
    let world = World { gpu: &gpu, ... };

    GraphicsEngine {
        gpu: gpu,
        world: world
    }
}

Which gives the error cannot return value referencing local variable gpu. Now I understand the error, but I wonder if there is really no way to do that. The only way around this I’m thinking of would be to not keep a reference to gpu in World and manually pass gpu every time a method of World is called, but this is super inconvenient. Plus it doesn’t convey the idea that World is “bound” to a single Gpu. Isn’t there any way to tell rust that the gpu field of GraphicsEngine will never change and thus the reference on it will always be valid?

Also I need to have gpu and world at the same place because some methods of GraphicsEngine impact both of them.

Thoughts?

EDIT: To add to this, I’m running into this problem all the time, not just in this particular case. I feel like either I’m thinking the wrong way or that it’s a “weak spot” in Rust.

#2

This is a case of self-referential struct. It is unsafe, and the borrow checker will not accept this on any variant of it.

The address you’ve put in world is to the let gpu variable on the stack, which becomes invalid as soon as you move the gpu to GraphicsEngine which lives at a different address, and will have yet another different address when you return it from the function.

Owned objects can be moved in Rust by memcpy, and Rust deliberately won’t keep track at runtime what references would need to be updated to object’s new address. This has many technical upsides, but the downside is that this kind of struct is not possible.

There are a couple of solutions:

  1. Use Rc<Gpu> or Arc<Gpu>. There’s no runtime cost to this, because you won’t be touching the refcount after creating the object.
  2. Take &Gpu as an argument to new_graphics_engine
2 Likes
#3

Thanks for the answer, I will use Rc then.

#4

You may be interested in this StackOverflow question.