#Help with an identifier system design in a Rust app
12 messages · Page 1 of 1 (latest)
I would recommend keeping your IDs and designing so that everything that looks up something by id is prepared for it being missing
a further disadvantage of the Rc<RefCell approach is that part of your UI might be displaying something that doesn't actually exist any more
better to explicitly make it vanish
May I ask why you panic instead of just returning Some<Track> or Result<Track,()> for a lookup?
That way you‘d be forced to validate that you actually found something before continuing.
I‘m pretty new to rust myself, so just ignore me if this is a stupid question.
I try to avoid things that panic as much as possible. So instead of unwrap I‘m using .ok_or_else(||Err(???))
You can just propagate an error up until you‘re at a point where you can handle it.
Not sure if that‘s what you mean, but that just sounds like an Arc<Mutex<T>> or Rc<Mutex<T>>
You can just pass around an object and modify it wherever as long as you have a lock.
While googling I also found this:
https://doc.rust-lang.org/std/sync/struct.Mutex.html#method.get_mut
A mutual exclusion primitive useful for protecting shared data
That method is extremely rarely useful
Basically, it says that if you're not actually sharing the mutex (it takes &mut), then you can get to its contents without locking
You could do a variant of option 3, where your IdMaps keep a reference count internally, and then you keep carrying around Ids. This way, remove decrements the refcount and only sometimes removes.
For this to work, you must have Id<T>: !Clone (as you have to update a refcount in the map)
This is probably slightly more convenient than rc/refcell dances: RefCell can be a pain to work with
The raii approach basically requires that you use rc, yes. Refcell is a pain, but I guess it works.
In an ideal world, this is where I'd suggest relevant types: dropping an id just doesn't compile, and you must pass it to remove as the only way to get rid of it. Rust doesn't have that feature, though. I guess you can panic on drop if you accept a suboptimal solution
Hypothetical that is unlikely to work: how about a scoped approach? Instead of something like let id = map.insert(thing);, how viable is map.with_id(thing, |id, map| { ... })? Then, you don't have remove at all, and just remove the thing when the closure returns. (The reason the closure also takes the map as an argument is so that you can call it again: with_id would mutably borrow it, so you have to do this trick).
I expect the answer to be "it doesn't work very well" (it might be unwieldy) but if it somehow works well it would solve a few issues: leaking is now impossible (everything has a clearly delimited removal point), and ids are only meant to be used inside their closure (you couldn't give ids a lifetime to enforce this, too)
I was unsure whether you need to share them between threads, hence the Arc. If you just have everything in the same thread, you can use Rc instead.
If you do need to share that data, but only need some of it to be mutable, you‘d normally implement the Mutex as low as possible.