I have an algorithm that was originally implemented in another language than rust.
This algorithm finds the right order of rendering for isometric objects. I have a goal of implementing this algorithm in rust.
I'll try to be short and not go into too much detail, but basically, in the original implementation, it works like this:
- Every isometric object is a dynamically allocated.
- Every isometric object has some isometric points, which are also dynamically allocated. Object have references to these points, and points have references to their object.
- There is a special container that holds points grouped by their depth. Actually it is a hash map of vectors of points, using depth as key. Every point holds a reference to the vector it is in, and every vector holds a reference to the hash map.
- When an object is added, all of its points are added to the container.
- When an object moves, its points are moved inside a container so that they are in the right place. This is done through the point object and uses references to the vector and to the hash map. Basically removes point from the original vector and adds to the right vector according to the new depth.
- When an object gets removed, all of its points are removed from the container. And here again, references from the point to the vector are very handy.
- To iterate objects in the right order we iterate over all vectors and over all of the points in these vectors. During this iteration, we get objects from points and do some things with them, like storing references to each other if one object is blocked from drawing by another object, but this is mostly outside of the scope of this question.
My previous rust experience is mostly solving Advent of Code puzzles, and this mostly did not involve any complex data structures. At least I've never used Rc<RefCell<_>>
, or may be used it once and do not remember it. I've read the documentation and I think I understand the concepts.
First of all, I've decided to implement a point container because it is a self-contained thing.
Some pseudo-code, so that I can speak in more detail:
struct Point {depth: Depth, owner: PointSubContainer};
struct PointContainer {map: HashMap<Depth, PointSubContainer>}
struct PointSubContainer{depth: Depth, owner: PointContainer}
impl Point {
fn new(depth, container) Point{depth, owner: container.get_or_create_sub_for_depth(depth)}
fn change_depth(depth) {
self.owner.remove(self);
self.depth = depth;
self.owner = self.owner.owner.get_or_create_sub_for_depth(depth);
self.owner.add(self);
}
fn dispose() {/*remove from owner*/}
}
Because points are referenced by both objects and containers I had to store them in Rc
, and because I have to modify them, that became Rc<RefCell>
. The same thing is with point containers and their sub-containers: they also became Rc<RefCell>
. I created most of the data structure and a code for adding points. I decided that container will store weak references to points and auto-remove them when point gets dropped. That was not very hard, but I felt like I lose the power of rust because now any reference to anything is actually mutable. But the main problems were when I needed to create an iterator over the container: Return an iterator from struct in RefCell - #7 by romamik.
Here is what I've got at the moment: Rust Playground
It all feels like I'm doing something wrong from the beginning. May I had not to store references to containers in points, but have the change_depth
method accept the root container as an argument and find actual sub-containers dynamically, that's more expensive than having references to them, but not too much. I will still have to have references to the points from both containers and objects, and that's a problem: I can imagine points being stored and owned by a container, like vector stores it's items for example, and having references to them in objects, but that will make container immutable because there always will be references to it's contents. I can eliminate references from points to objects, and find points dynamically when I need to modify this, but that can be expensive at runtime.
So, what is the rust approach to such algorithms?