Objects by ID, pass the SlotMaps everywhere, or static mut?

I'm working on a library in which I have a bunch of Widget types (Label, Button, Switch, VStack, List, etc), and I'm currently storing Widgets in SlotMaps, keyed by WidgetId. My widget slot-maps are currently part of root Window, so I can find a widget with a Window+WidgetId pair, which in other languages might be just a Widget object pointer.

My question is, should I pass the Window around everywhere, or should I move my widget slot-maps to a static mut container, so that I only pass around WidgetIds? I thought static muts where "bad", but passing around the Window also seems awkward. Maybe it's just that I need to get used to it, because it's different from other languages.

[edit to clarify: widgets vs widget types]

static mut is bad. If you decide to have global state, use OnceLock (or LazyLock), with a Mutex or RwLock if needed. Those were added to std in large part because even experts tend to create UB with static mut.

Another alternative to passing around is to use Arc<Mutex<_>> or Rc<RefCell<_>> so that everything that needs to query Window shares ownership of the Window.

Another pseudo-alternative to passing around is to implement things on "view types" that hold a reference to Window (so you call methods on the view type instead of explicitly passing a reference to Window). Though it can sometimes lead to borrow checker headaches.

2 Likes

You're talking about two different kinds of look-ups.

Storing the widget type in a static, read-only map seems like a good idea, assuming there is a fixed number of them and you can create them up front.

Finding the widget requires the window, so you have to pass the window, right? And storing the window in a global probably doesn't make sense. Or do I misunderstand?

Let me edit my question - sorry. I'm storing the widgets in the window, not the types. I mean I have lots of types of widgets, like Labels, Buttons, VStacks, Checkboxs, etc.

Then using a static map doesn't make sense to me. These are objects that can come and go, so they don't necessarily exist for the entire lifetime of the program.

I guess I'm also not entirely sold on this object-ID and slot-map design. I've been working with it but haven't seen much benefit yet. My experience for other languages makes me want to just have "objects", so Rc<RefCell<dyn Widget>>.

Yeah, the Widgets come and go, though maybe not quickly, as they represent a UI on screen. The owner/container/slot-map though could be there for the entire program.