/// intentionally does not implement copy or clone /// which should make it harder to misuse; /// the idea is to generally try to guarantee all IDs /// point to something valid, although duplicate /// gets around this if needed #[derive(Eq, Hash, PartialEq, Debug)] pub struct Id(u64); #[derive(Default)] pub struct IdTracker { free: Vec, cur: u64, } impl IdTracker { #[allow(clippy::should_implement_trait)] pub fn next(&mut self) -> Id { if let Some(id) = self.free.pop() { return id; } let id = Id(self.cur); self.cur += 1; id } #[allow(dead_code)] pub fn free(&mut self, id: Id) { self.free.push(id); } } impl Id { /// this must be used carefully to make sure /// all IDs are still valid references; /// named weirdly to indicate this. /// generally should not be used in "user" code pub fn duplicate(&self) -> Self { Self(self.0) } /// this must be used carefully to make sure /// all IDs are still valid references. /// generally should not be used in "user" code pub fn copyable(&self) -> CopyId { CopyId(self.0) } } #[derive(Eq, Hash, PartialEq, Debug, Clone, Copy)] pub struct CopyId(u64); impl CopyId { pub fn id(&self) -> Id { Id(self.0) } } pub trait IdUtil { fn duplicate(&self) -> Self; } impl IdUtil for Option { fn duplicate(&self) -> Self { self.as_ref().map(|i| i.duplicate()) } }