Learning Rust : Orbitals

How do I store non-owning references to collection members?
< Home >

I want to make a data-driven viewer for the solar system, as part of a game inspired by Aurora 4x but Rust is an entirely different beast from C.

So let's start with the types i'll use:


pub const GRAVITATIONAL_CONSTANT : f64 = 6.67430e-11;

pub type Second = u64;
pub type Meter = f64;
pub type Rad = f64;
pub type Kilogram = f64;
            

I have prior experience writing systems like this, so I figured it would be as easy as:


pub struct Orbital {
    //Some optional non-owning reference to the parent orbital
    mass : Kilogram,
    semi_major_axis : Rad,
    eccentricity : Rad,
    inclination : Rad,
    long_asc_node : Rad,
    argument_periapsis : Rad,
    mean_anomaly_epoch : Rad
}
            

The question then is: How do we store the reference to the parent orbital?

note: by parent orbital, I mean the orbital body that the given orbital body orbits.

In C, we would just store a pointer into the data of the owning container (i.e. a dynamic array).

I understand that the method above could cause problems with object lifetimes and dead references, but in this case we are assuming a static lifetime with a fixed-width container.

So my first instinct is to look in the Rust docs for a non-owning reference type.

I wasted about a day researching Rc's and RefCell's when I had a revelation; what I wanted was over-engineered. Instead of storing a reference to the parent orbital, I could just store an index into the system's orbital container. All I need to do to make this work is pass a borrowed ref to the system object to the orbital's update function.

But not every part of the update function needs the parent orbital's position, so I can split the update into two parts:

1: Update the orbital elements relative to the parent object

2: Use the parent orbital's position to transform the relative coordinates into absolute coordinates

All the tick method needs is an immutable parent object to calculate the standard gravitational parameter of the one-body system being emulated.

So here's what I came up with.


pub struct Orbital {
    origin : Option<usize>,
    //Euler angles for a given time (T) relative to origin
    rel_pos : Option<(Second, Meter, Rad, Rad, Rad)>
    //Cartesian coordinates for a given time (T) relative to (0, 0, 0)
    abs_pos : Option<(Second, Meter, Meter, Meter)>
    //Orbital parameters
    mass : Kilogram,
    semi_major_axis : Meter,
    eccentricity : Rad,
    inclination : Rad,
    long_asc_node : Rad,
    argument_periapsis : Rad,
    mean_anomaly_epoch : Rad
}
//...
impl Orbital {
//...
    //Update relative 
    pub fn tick(&mut self, &system: System, t: Second) {
        //...
    }
    
    pub fn pos(&self, &system : System, t: Second) -> (Meter, Meter, Meter) {
        //...
    }
}
        

Now comes the daunting task of storing our orbitals in a usable manner. Ideally, I want to iterate through the list of orbitals in a way that also lets me access other members of the orbital list from the tick and pos functions. It's a lot harder than it should be.