Generate sequential IDs for each instance of a str

2020-02-06 03:02发布

问题:

I'm writing a system where I have a collection of Objects, and each Object has a unique integral ID. Here's how I would do it in C++:

class Object {
public:
  Object(): id_(nextId_++) { }

private:
  int id_;
  static int nextId_;
}

int Object::nextId_ = 1;

This is obviously not thread_safe, but if I wanted it to be, I could make nextId_ an std::atomic_int, or wrap a mutex around the nextId_++ expression.

How would I do this in (preferably safe) Rust? There's no static struct members, nor are global mutable variables safe. I could always pass nextId into the new function, but these objects are going to be allocated in a number of places, and I would prefer not to pipe the nextId number hither and yon. Thoughts?

回答1:

nor are global mutable variables safe

Your C++ example seems like it would have thread-safety issues, but I don't know enough C++ to be sure.

However, only unsynchronized global mutable variables are trouble. If you don't care about cross-thread issues, you can use a thread-local:

use std::cell::Cell;

#[derive(Debug)]
struct Monster {
    id: usize,
    health: u8,
}

thread_local!(static MONSTER_ID: Cell<usize> = Cell::new(0));

impl Monster {
    fn new(health: u8) -> Monster {
        MONSTER_ID.with(|thread_id| {
            let id = thread_id.get();
            thread_id.set(id + 1);
            Monster { id, health }
        })
    }
}

fn main() {
    let gnome = Monster::new(41);
    let troll = Monster::new(42);

    println!("gnome {:?}", gnome);
    println!("troll {:?}", troll);
}

If you do want something that works better with multiple threads, check out bluss' answer, which shows how to use an atomic variable.



回答2:

Atomic variables can live in statics, so you can use it relatively straightforwardly (the downside is that you have global state).

Example code: (playground link)

use std::{
    sync::atomic::{AtomicUsize, Ordering},
    thread,
};

static OBJECT_COUNTER: AtomicUsize = AtomicUsize::new(0);

#[derive(Debug)]
struct Object(usize);

impl Object {
    fn new() -> Self {
        Object(OBJECT_COUNTER.fetch_add(1, Ordering::SeqCst))
    }
}

fn main() {
    let threads = (0..10)
        .map(|_| thread::spawn(|| Object::new()))
        .collect::<Vec<_>>();

    for t in threads {
        println!("{:?}", t.join().unwrap());
    }
}


标签: rust