6 ms·
I found this excellent talk to be complementary to my talk[1] on data-oriented GUI in Rust, not to plug my own work too much. I found a lot of common ground:
by raphlinus 8y ago
I found this excellent talk to be complementary to my talk[1] on data-oriented GUI in Rust, not to plug my own work too much.
I found a lot of common ground:
* Trying to write object-oriented code in Rust doesn't work well. Writing data-oriented code does.
* "Fighting the borrow checker" is a sign you might be doing it wrong. Try data-oriented approaches instead.
* Use a structure-of-arrays rather than an array-of-structures.
* Use what I call "state splitting" to borrow mutable references to just the state you need; this works well with the structure-of-arrays approach and is a powerful motivator to use it.
* To build graph-like structures, reach for a Vec of components, and indexes into the Vec for relationships, as your first choice. Self-references, arena allocators, and Rc are viable alternatives, but you should have a good reason to use them instead of Vec.
* Some form of dynamic typing is useful to keep your system loosely coupled (though we ended up with very different forms of dynamic typing, see below).
* Data-oriented approaches have taken root in the C++ gaming community, mostly motivated by performance, but adapt well to Rust, and some of the ideas may be useful in domains beyond gaming.
There were some other points that went beyond what I talked about, but could fit in well:
* A "generational index" is a good way to avoid use-after-free style errors that result from the use of a stale index. The slotmap crate can help.
And now for the things that are different.
* The exact nature of dynamic typing is different. An ECS usually uses a registry of the different component types (anymap is a useful crate) and is quite open-ended in adding new types of components. My xi-win-ui, by contrast, has two main component types, Widget and listener, and does dynamic dispatch on a Widget trait.
This naturally raises the question, which is better? From my perspective, both are valid. The pure ECS approach definitely makes sense when the components are interacting with each other in diverse ways (collisions, damage, etc). In a GUI, the components are mostly interacting with the framework, and have diverse behaviors within standardized interfaces (input, layout, paint).
Thus, one point of my talk is that you don't have to reject all vestiges of object oriented programming, it's perfectly reasonable to hybridize, using data-oriented approaches for state splitting and graph structure, and dynamic dispatch for the behavior variation.
My talk also went deeper into the ideas of "data flow rather than control flow" and the use of an event or command data structure rather than method calls. I'm not sure how deeply these ideas apply to games, but wouldn't be surprised if they do.
[1] video: https://www.youtube.com/watch?v=4YTfxresvS8 https://www.youtube.com/watch?v=4YTfxresvS8 , slides: https://docs.google.com/presentation/d/1aDTRl5R-icAF38Di-qJ4FzAl3pLlutTKVFcr3mUGgYo/edit?usp=sharing https://docs.google.com/presentation/d/1aDTRl5R-icAF38Di-qJ4...
- anderspitman 8y agoI highly recommend watching Raph's talk for anyone writing Rust apps that manage non-trivial state. Ah heck just watch it no matter what it's great.
- raphlinus 8y agoAww, thanks :)
- MichaelMoser123 8y ago# Use a structure-of-arrays rather than an array-of-structures. Could you please explain this in more detail? # To build graph-like structures, reach for a Vec of components, and indexes into the Vec for relationships And that also allows you to reference deleted nodes?
- steveklabnik 8y ago> Use a structure-of-arrays rather than an array-of-structures. instead of this: struct World { players: Vec<Player> } struct Player { name: String, health: i64, } which is a "array of structures", see Vec<Player>, you do this: struct World { player_names: Vec<String>, player_health: Vec<i64>, } "A structure of arrays". "Player zero" is no longer an index into a players array, but an index into many arrays, all of which hold certain kinds of data about a player. > And that also allows you to reference deleted nodes? ... which is why the talk then references generational indices as a way of dealing with it.
- philipov 8y agoThis sounds like it is just going to trade one set of problems for another. It makes it impossible to write generic container types. What if elements of the same collection need to have different structure? What benefits justify this extremely tight coupling?
- steveklabnik 8y ago