Yes, but...
Yes, it's possible. If you're in a hurry, here it is. It involves unsafe code and hence it's your job to make sure there aren't any data races if your application is multithreaded. But please read on.
static mut NEXT_ID: u32 = 0;
struct User {
name: String,
id: u32,
}
impl User {
// WARN: Not at all thread-safe!
unsafe fn new_user(name: String) -> User {
let id = NEXT_ID;
NEXT_ID += 1;
User {
name,
id,
}
}
}
However, this is not thread safe. It's not reentrant. It's very difficult to test or mock the ID values. It's brittle and causes a lot of problems. So in addition to the word unsafe being a bad idea in general, it's also just not good coding style.
What you should do is wrap that global up into another struct.
#[derive(Clone, Default)]
struct UserFactory {
next_id: u32,
}
impl UserFactory {
fn new() -> UserFactory {
UserFactory::default()
}
// Assuming the definition of struct User from above...
fn new_user(&mut self, name: String) -> User {
let id = self.next_id;
self.next_id += 1;
User { name, id }
}
}
Now UserFactory is a data structure and your caller can determine how local or global it should be. If they want to declare one in main, that's their business. But if they want three different user databases running in parallel, they can make three UserFactory instances. It's guaranteed thread-safe, since Rust's borrowing rules ensure only one mutable borrow of a data structure exists at a given moment. And if we really wanted to be thorough (if this is an enterprise application, not a toy environment), we could make a trait
trait UserConstructor {
fn new_user(&mut self, name: String) -> User;
}
and make UserFactory implement that trait. Then we can even mock it in tests by making a fake UserFactory that, say, always returns zero or that returns some designated numerical values needed in our tests.
And since traits (excluding dyn, but we're not using that here) are resolved at compile-time, there's zero overhead to throwing this function in a trait. At runtime, it's still a static early-bound call to a known function.