I have a simple example game entity called a Nation, which is a struct that contains all the data relevant to the player's assets and activities. The player is allowed to change their nation name, which is used to log in and take their turns. Thus, the player has to have a unique identifier that doesn't change.
There are also other things in the game system which similarly need unique identifiers, so to my dinosaur C brain, the obvious solution was to try making a global HashMap to contain the struct name as a key, and the instance count... and then auto-increment that counter whenever a new struct is created, using the current value as the ID.
Rust really hates me for trying that, which is fine. But, I'd like to learn a more proper way to accomplish this. Since I have to implement Default for these anyways, putting the increment and then assignment in the default() function seems like the right place to use it, but the counters themselves need to live somewhere accessible to those functions.
Right now, I have this, which is ugly but seems to work, so far:
static mut nation_id : i64 = 0;
#[derive(Debug)]
struct Nation {
id : i64, // unique naiton ID, established at allocation
name : String, // the nation name, entered at runtime
// more stuff here...
}
impl Default for Nation {
fn default() -> Nation {
unsafe {
nation_id += 1;
Nation {
id: nation_id, // We want this to actually come from a counter
name: String::from(""),
// more stuff here...
}
}
}
}
// World just holds a Vec<Nation> that is initialized to new() and thus empty.
fn main() {
println!("This is just a test.");
let mut w : World = Default::default();
println!("{:?}", w);
println!("{} nations exist.", w.nations.len());
let mut n : Nation = Default::default();
println!("{:?}", n);
w.nations.push(n);
println!("{} nations exist.", w.nations.len());
let mut n2 : Nation = Default::default();
println!("{:?}", n2);
w.nations.push(n2);
println!("{} nations exist.", w.nations.len());
println!("Test completed.");
}
If you want a counter, one way to do that is to use atomics. This function will return a counter and can work on multiple threads concurrently.
use std::sync::atomic::AtomicU64;
use std::sync::atomic::Ordering::SeqCst;
pub fn unique_id() -> u64 {
static COUNTER: AtomicU64 = AtomicU64::new(0);
COUNTER.fetch_add(1, SeqCst)
}
We can also improve it by enforcing that ids must be unique.
pub fn unique_id() -> u64 {
static COUNTER: AtomicU64 = AtomicU64::new(0);
let id = COUNTER.fetch_add(1, SeqCst);
assert_ne!(id, u64::MAX, "ID counter has overflowed and is no longer unique");
id
}