i'm trying to learn rust by porting an old typescript project of mine, and i'm struggling to figure out how i want to structure my data. i hope this isn't too long/specific/verbose...
background: i'm building 4 library crates. 3 implement different glicko rating systems (glicko, glicko-2, glicko-boost), and the 4th is a dependency containing shared logic and other common stuff. you can choose to install any of the 3 systems, and you always need the shared lib.
in my ts version, all players had a rating and rating deviation, but glicko-2 players also required a 3rd value called sigma, like so:
export interface Player {
r: number;
RD: number;
sigma?: number;
}
export interface FullPlayer extends Player {
sigma: number;
}
this let common functions accept Player (you can still pass FullPlayer because duck typing), while functions that required sigma could ask for FullPlayer.
unfortunately it does not end there. ratings can exist on 2 different scales, like Kelvin vs °C. the internal glicko-2 scale is better for computation, but less human readable. all 3 algorithms operated on this internal scale.
-# (if you've ever looked into the glicko papers before, that last statement might seem wrong, but dw about it)
when processing many millions of matches (which can absolutely happen since glicko is batched), there's a certain cost to converting every user in every match between the scales, at least twice. so allowing users to just store them in the internal scale and only convert when displaying data would allow skipping unnecessary conversions.
because of this, my ts implementation also defined:
export interface ScaledPlayer {
mu: number;
phi: number;
sigma?: number;
}
export interface ScaledFullPlayer extends ScaledPlayer {
sigma: number;
}
most functions accepted Player | ScaledPlayer and/or FullPlayer | ScaledFullPlayer and returned the same type they were passed, using funky overloads to preserve the scale.
this is obviously not something i can translate directly into rust, but i'm not sure what the right approach is...
- sigma being required by some functions could be modeled with
Option<f64>, but that seems to trade compile-time safety for runtime checks - allowing players to exist in either scale (and skipping conversions when possible) seems hard, and i don't think rust has union types like ts. there might be some way using generics and phantomdata, but i haven't really figured that out
what would be the idiomatic rust way to handle this? i expect i'll have to do things very differently