Project architecture, and global objects

I wont to port my JS framework to WASM via Rust and have some questions about architecture. In most of engines and frameworks that I use some utils are global and can be reached from every place of code, they stored via global static object or static class, that have links to some services. For example I need only one instance of storage and manage textures, text data and etc, loader that have access to storage and can manipulate with it, global event dispatcher that can pass events via some not connected elements and etc. I read that I can do something like this via lazy_static crate, but is it right way? Or do you have some other approach?

1 Like

lazy_static is a good solution if you really want a global, shared object.

However, design patterns from languages that allow anything to reference anything anywhere are not well suited for Rust. Rust wants you to carefully think about what can reference what, and limit scope of that as much as possible.

The Rust language assumes all programs are multi-threaded. WASM, despite not having threads yet, doesn’t get special treatment in this regard, so the language will bug you about thread synchronization even in WASM. Everything global has to be thread-safe (wrapped a in Mutex or such), which will add boilerplate and overhead, going against the convenience you wanted from using globals.

Globals will also make testing more complicated, since Rust’s built-in framework prefers to run tests in parallel.

1 Like

@kornel Thanks for answer.
Maybe you have own vision how to organize it? Cause I can’t understand how two structures can communicate one each other if they are not in same level (if they have 5 levels of difference in this case I need pass message through 5 struct that do something else)? Now WASM support threads in experimental status, in future I wont move rendering to own thread.

That’s right; in general, the more idiomatic thing in Rust is to either not need to call something 5 levels of abstraction away, or to have passed it as part of the construction through each of those levels. It’s a nearly-functional way of thinking about the flow of data: make the arguments direct and explicit; don’t have implicit dependencies from global statics. Here, in Rust, it’s because that makes data-race problems able to be analyzed by the compiler.