It looks like you're just asking about traits, a few specific implementations of this trait, and a wrapper object that restricts itself to types that implement that trait. If desired, the container can implement the attribute, delegating it to the internal object.
trait Health { fn life(&self) -> u8; fn hit_for(&mut self, lost_life: u8); } #[derive(Debug, Copy, Clone)] struct WimpyHealth(u8); impl Health for WimpyHealth { fn life(&self) -> u8 { self.0 } fn hit_for(&mut self, lost_life: u8) { self.0 -= lost_life * 2; } } #[derive(Debug, Copy, Clone)] struct BuffHealth(u8); impl Health for BuffHealth { fn life(&self) -> u8 { self.0 } fn hit_for(&mut self, lost_life: u8) { self.0 -= lost_life / 2; } } #[derive(Debug, Copy, Clone)] struct Player<H> { health: H, } impl<H> Health for Player<H> where H: Health { fn life(&self) -> u8 { self.health.life() } fn hit_for(&mut self, lost_life: u8) { self.health.hit_for(lost_life) } } fn main() { let mut player_one = Player { health: WimpyHealth(128) }; let mut player_two = Player { health: BuffHealth(128) }; player_one.hit_for(12); player_two.hit_for(12); println!("{:?}", player_one); println!("{:?}", player_two); }
it is impossible to have an array of such Players without using boxed values
It is right. An array or vector (or any general type, really) must be of the same type. This is especially important for arrays / vectors because their memory layout is continuous and each element must be in a fixed interval.
If you were allowed to have different types, then you may have one player who had health, which took 1 byte, and another player with health took 2 bytes. Then all offsets will be wrong.
You can implement the Health attribute for Box<Health> , and then Player objects can be saved sequentially, but they have a pointer to the corresponding specific Health implementation through the field.
impl<H: ?Sized> Health for Box<H> where H: Health { fn life(&self) -> u8 { (**self).life() } fn hit_for(&mut self, lost_life: u8) { (**self).hit_for(lost_life) } } fn main() { let mut players = vec![ Player { health: Box::new(WimpyHealth(128)) as Box<Health> }, Player { health: Box::new(BuffHealth(128)) as Box<Health> } ]; for player in players.iter_mut() { player.hit_for(42); } println!("{:?}", players[0].life()); println!("{:?}", players[1].life()); }