I've come across a similar problem for a project.
For my usecase I found the usage of singletons to come pretty far for me in rust. It is probably somewhat dirty, but given that the scope of my project grew with it over time, I found it pretty useful there. If you know most of the architecture ahead of time, doing a careful architecture is probably the best, but if the scope can change quickly while you already have a lot of code, redoing the entire architecture along with the program might be too cumbersome.
For my usecase, I basicially created a file/mod for certain global resources. Each contained a main struct with a private constructor function that is usually only called once. The resource was then wrapped in a
RwLock (basically a more efficient Mutex) to access this resource from anywhere. The instance itself is stored in a private lazy_static and is accessible by the pub functions
instance_mut(). SO kinda a singleton.
It is pretty dirty, but has the advantage of being pretty similar to the likes of Java and similar. And if you get some new fringe case to access this data from somewhere, you needn't worry about having it passed to you. It would basically be a
category::some_mod::instance().fringe_data() from anywhere.
There is also the concept of "inner mutability" in rust. It basically means that you put changeable attributes of a struct in a Mutex and thus allow mutation of data over an immutable borrow of the struct's instance. That way you can use an Arc instead of a Mutex or RwLock and it might might access more efficient of lots of mutable accesses are happening to only parts of your struct. Though at that point, the struct is probably doing too much anyway and should get split up.
I haven't considered Indices/Databases but those could be a more idiomatic replacement for rust as well.
You also may want to consider whether to use async or not. If you're not used to async, probably try to not use it for the time being. But since I used actix-web for the REST it proved valuable for me to make most of my architecture async (tokio has a lot of drop in replacements for Mutex/RwLock and the likes).
If you need to wait for events, you may also consider a Bus or Broadcast channel to allow other components to subscribe to certain changes/events of your singleton. Though going this route will probably make the relations even messier.
So please don't hate me on this proposal. I know it is pretty dirty and probably not much better than what C++ devs or some not-so-thought-out Java Programs use, but especially If your software is changing while it's being developed, I found this pattern (Singleton and Channels with optionally a focus on async/tokio) pretty useful and easy. I think this the message passing is a bit similar to actix's actor model (or maybe what go channels do, no experience there though).