Where add just pushes a boxed entity to the back of the vector. And the entities cant be mutated after adding but i can add entities that have references to previously created entities.
Ideally i would like to write an add method which constructs a new entity and returns a stable reference to it that others can use that lasts the lifetime of the Scene object
However this (rightly) complains that my output type needs to live as long as the mutable borrow of self. If i extend the mutable borrow of the self to be as long as the scene exists for (&'scene mut self) then this method works but i can only ever add one item to my scene.
Ideally i would want to make the output reference last as long as the scene however i understand why we can't (if some other theoretical holder of a mutable reference to entities could drop by pinned box early and invalidate my reference.
It is possible that I have just ended up confusing myself and there is a easy way to convince the compiler that as we only add to the vector I can extend the reference of the element to the parent type. Or it could be that i am just being dumb and the idea cant really work - is unsound.
There is a couple of ways around it (the most basic one being wrapping all references in ref counted pointers and move the checking to runtime)
That's a self-referencial struct. TL;DR, references (&, &mut) are the wrong tool for the job. Also, Pin doesn't magically make self-referencial things work without unsafe. And Box is probably also best avoided as your borrows are invalidated when the Vec resizes (moves the Boxes).
There's no solution using actual references without unsafe,[1] and with unsafe it's notoriously hard to get right. You're better off with raw pointers if you try to tackle it yourself.
There are also some self-referencial crates that try to be sound (with a history of failures and potentially outstanding unsoundness). ouroboros and yoke are the two I've seen most frequently. I can't vouch for them, but they exist.
well, except the "borrow forever" approach which is pretty much never adequate ↩︎
Although (without unsafe) there is no way to have a self-referential struct using references, you can of course use indexes instead. This is a very common solution. In general, using indexes (or other types of keys) rather than references in data structures is something that quickly becomes a habit when using Rust. By design, Rust references are meant to be short lived.
I had a version with indices however i was a bit worried that i was just using that to get around lifetime tracking. If i am storing indices there is still a lifetime is dependence (is this id still pointing at the object i thought it is and is that still valid) but it needs to be proved manually.
Sure i will take panics over inscrutable memory corruption any day!
As I said the previous code was all pointer based from C++ so i am not loosing anything with the change - I was just checking to see if I could find a way of the compiler helping me more!
By the way, despite the name, Rust lifetimes ('_ things) are generally about the duration of borrows and not value liveness.[1] There's also no direct connection with the lifetime of a borrow and the, how to say, identity of an object...[2] though I suppose you did account for that aspect with Pin and being append-only, consciously or not.
A Scene<'scene> doesn't have to exist for all of 'scene, for example, and a Box<dyn Trait + 'static> need not be leaked.
It'd be less confusing if they had a different name, but here we are. ↩︎