Let's say I have a struct Mesh that holds a reference to a struct Geometry, making the below possible.
let geometry = Geometry::new();
let mesh1 = Mesh::new(&geometry);
let mesh2 = Mesh::new(&geometry);
let mesh3 = Mesh::new(&geometry);
Then let's say I want to have a struct Player that holds a Geometry and a Mesh. The most natural way (at least for me) would be to have the player own a Mesh and Geometry and encapsulate creating them in the Player struct like so:
pub struct Player<'a> {
geometry: Geometry,
mesh: Mesh<'a>
}
impl<'a> Player<'a> {
pub fn new() -> Self {
let geometry = Geometry::new();
let mesh = Mesh::new(&geometry);
Self {
geometry,
mesh
}
}
}
This results in a compile time error
cannot return value referencing local variable geometry
returns a value referencing data owned by the current function
Which makes sense to me if I was only returning a Mesh, but I'm returning a Mesh and the Geometry it refers to together. That way, the geometry is moved out of the function scope so it won't be deallocated to invalidate the reference. Either I'm misunderstanding something or Rust isn't smart enough to recognize this should be okay.
This is a scenario that will happen a lot with my project, having a struct that owns both a Geometry and a Mesh. So my question is, what are some suggestions I could implement that would fix this problem?
I can see two options, neither of which I really like.
Option 1 - Have Mesh own a Geometry
This would eliminate the reference but many Meshes would then not be able share a single Geometry. Not ideal if you have a large Geometry you want to be used by many Meshes since you would have to create multiple copies of the same Geometry.
Option 2 - Lift the Geometry to a higher scope
The Player's new function could take in the geometry as a reference but now you'd have to create it outside the Player so you've lost the nice encapsulation of keeping it inside and now more work is required to create a Player struct. You could alleviate this a little bit by changing the Player to the following.
pub struct Resources {
geometry: Geometry
}
pub struct Player<'a> {
mesh: Mesh<'a>
}
impl<'a> Player<'a> {
pub fn create_resources() -> Resources {
Resources {
geometry: Geometry::new()
}
}
pub fn new(resources: &'a Resources) -> Self {
let mesh = Mesh::new(&resources.geometry);
Self {
mesh
}
}
}
This way the creation of the Geometry is sort of encapsulated in Player. But you still have the undesired pattern of needing to call create_resources() before calling new() and moving ownership of the Geometry to a higher scope.
Is there a better option that allows both to hold:
- A Mesh holds a reference to a Geometry
- The Mesh and Geometry are fully encapsulated in the Player
Can I satisfy both or will I need to make a sacrifice?