Rust: Turning a custom struct into a persistent data structure?


#1

I am working on a small open-source game. Most of the server- and networking-logic is written in Elixir, but the actual game logic is written in Rust.

However, to not cheat with the immutable nature of the data structures that are used under the hood in the Rust code, I would like to use persistent data structures instead of mutable ones such that even though these structures are opaque from the Elixir side, passing the same opaque value twice to a ‘render’ Rust function twice should result in the same output.

For persistent lists, vectors, sets and maps, there is the great rpds library.
But now I have the issue that I would like my actual game state struct to become a persistent data structure.

I see te following possibilities:

  1. Don’t do it, only have the members of the struct be persistent data structures and create a new copy of the main game state struct every time one of its fields is changed.
  2. Emulate the struct as a RPDS map. This would mean that the different fields of the struct are now strings whose existence is no longer checked by the compiler.
  3. Write custom logic to make it persistent.

What to do?


#2

Lots of people read this topic, but nobody answered.

I wonder: Is my question extremely specialized or hard to answer?

If that is the case, please do tell me what is unclear, and I’ll try to explain it better :slight_smile: .


#3

It is more suited to a game developer forum rather than anything extremely Rust specific.

Also should state more why you want the state in persistent data rather than mutable. To me that would seem to be just be a performance hit without benefit, but not something I’m familiar with so just a guess rather than experience.


#4

Isn’t option one exactly how you’d implement a persistent data structure?


#5

It’s a little more complicated in Rust, since there’s owned data.

In languages like Haskell and Clojure, it doesn’t make sense to replace a regular old record (struct) with a persistent data structure, because they usually have such a small fixed number of fields. You only want to use persistent data structures for the “arbitrary” collections, like big lists or maps. Every field is a reference in those languages, so it’s very very cheap to copy the whole thing and change one field when you want to make a change.

I would recommend doing the same in Rust, but you probably want to make sure that fields pointing to large data are either references, Rc, or Arc. That way they’re sharing data with other instances instead of having everything deep-copied. If it’s something small like a short String, then don’t worry about it.


#6

… so, to put it in terms of your options, I would say go with “1”, but remember to put your rpds fields in Rc<>s. (unless those rpds data structures are already implicitly RC’d? I’m not sure, after a quick glance at the docs).


#7

@quadrupleslap usually when people refer to persistent data structures, they are talking about values with structural sharing. Copying and mutating the new version is technically persistent, but more common data structures are optimized to share the unchanged parts of the structures.


#8

This is a smart idea! I think this is exactly what I am looking for :smiley: .


#9

Another idea for persistent data would be to make use of my new internment crate, which gives you an efficient data sharing version of Rc.


#10

internment looks nice, but could you explain in more detail what the difference of ArcIntern is with relation to a normal Arc-ed structure? Is the idea that when you re-create a structure with the same state twice in possibly different locations, that these instances then are also shared?


#11

That is exactly the difference. And it had the result that comparing two ArcIntern<T> is as costly as comparing two pointers. Similarly, hashing an ArcIntern<T> is as cheap as hashing a pointer, while maintaining the semantics of hashing the content.