/// Here you can find the `cargo expand` output of the `simple_example.rs` test file. /// This is a rough `expansion`, ignores some minor details for readability, /// and does not expand irrelevant parts of the code (e.g. `#[derive(Debug)]`, etc.) /// /// This file serves the purpose of revealing what's happening behind the curtains. use core::marker::PhantomData; #[derive(Debug)] struct Player { race: Race, level: u8, skill_slots: u8, } #[derive(Debug, PartialEq)] enum Race { Orc, #[allow(unused)] Human, } struct PlayerBuilder { race: Option, level: Option, skill_slots: Option, #[allow(unused_parens)] _state: PhantomData State1>, } mod sealed { pub trait Sealed {} } pub trait TypeStateProtector: sealed::Sealed {} struct Initial; struct RaceSet; struct LevelSet; struct SkillSlotsSet; impl sealed::Sealed for Initial {} impl sealed::Sealed for RaceSet {} impl sealed::Sealed for LevelSet {} impl sealed::Sealed for SkillSlotsSet {} impl TypeStateProtector for Initial {} impl TypeStateProtector for RaceSet {} impl TypeStateProtector for LevelSet {} impl TypeStateProtector for SkillSlotsSet {} impl PlayerBuilder { fn new() -> Self { PlayerBuilder { race: None, level: None, skill_slots: None, _state: (PhantomData), } } } impl PlayerBuilder { fn set_race(self, race: Race) -> PlayerBuilder { PlayerBuilder { race: Some(race), level: self.level, skill_slots: self.skill_slots, _state: (PhantomData), } } } impl PlayerBuilder { fn set_level(self, level_modifier: u8) -> PlayerBuilder { let level = match self.race { Some(Race::Orc) => level_modifier + 2, // Orc's have +2 level advantage Some(Race::Human) => level_modifier, // humans are weak None => unreachable!("type safety ensures that `race` is initialized"), }; PlayerBuilder { race: self.race, level: Some(level), skill_slots: self.skill_slots, _state: (PhantomData), } } } impl PlayerBuilder { fn set_skill_slots(self, skill_slot_modifier: u8) -> PlayerBuilder { let skill_slots = match self.race { Some(Race::Orc) => skill_slot_modifier, Some(Race::Human) => skill_slot_modifier + 1, // Human's have +1 skill slot advantage None => unreachable!("type safety ensures that `race` should be initialized"), }; PlayerBuilder { race: self.race, level: self.level, skill_slots: Some(skill_slots), _state: (PhantomData), } } } impl PlayerBuilder where A: TypeStateProtector, { fn say_hi(self) -> Self { println!("Hi!"); self } } impl PlayerBuilder { fn build(self) -> Player { Player { race: self.race.expect("type safety ensures this is set"), level: self.level.expect("type safety ensures this is set"), skill_slots: self.skill_slots.expect("type safety ensures this is set"), } } } fn main() { let player = PlayerBuilder::new() .set_race(Race::Orc) .set_level(1) .set_skill_slots(1) .say_hi() .build(); println!("Race: {:?}", player.race); println!("Level: {}", player.level); println!("Skill slots: {}", player.skill_slots); }