Rust (vs C# and vs. Julia and other) at least relating to game dev: "Leaving Rust gamedev after 3 years" and ECS

https://loglog.games/blog/leaving-rust-gamedev/

It’s an interesting post on Rust, not just for game dev, though mostly (does it translate to other domains, e.g. science? Seemingly to other exploratory programming), but I note also sections “GUI situation in Rust is terrible” and “Positives of Rust” (“If it compiles it often kinda just works. This is both a meme but in some sense not really.”), nor is it just about [Bevy’s] Entity Component System (but such systems could be good in Julia, or not, or other languages):

https://bevy-cheatbook.github.io/programming/ecs-intro.html

That being said, there is an overwhelming force in the Rust community that when anyone mentions they’re having problems with Rust the language on a fundamental level, the answer is “you just don’t get it yet, I promise once you get good enough things will make sense”. This is not just with Rust, if you try using ECS you’re told the same thing. If you try to use Bevy you’ll be told the same thing. If you try to make GUIs with whichever framework you choose (be it one of the reactive solutions or immediate mode), you’ll be told the same thing. The problem you’re having is only a problem because you haven’t tried hard enough.

I believed this for years. I tried, very hard, for years. […]

But, and I say this having spent the past ~3 years and written over 100k lines of game-related code in it across the whole ecosystem of frameworks/engines and having made my own, many if not most of the problems don’t go away if one isn’t willing to constantly refactor their code and treat programming as a puzzle solving process, rather than just a tool to get things done.
[…]
My take is, while that is 100% true, there’s a fundamental problem of games being complex state machines where requirements change all the time. Writing a CLI or a server API in Rust is a very different experience than writing an indie game. Assuming the goal is to build a good experience for players rather than an inert set of general purpose systems, the requirements might change from day to day just after having people play the game and you realize some things need to fundamentally change. Rust’s very static and overly-checked nature fights directly against this.
[…]
I’d argue as far as maintainability being the wrong value for indie games, as what we should strive for is iteration speed. Other languages allow much easier workarounds for immediate problems without necessarily sacrificing code quality.
[…]
What would be 3 lines of code in C# suddenly becomes 30 lines of Rust split into two places.
[…]
ECS solves the wrong kind problem

Because of the way Rust’s type system and borrow checker works, ECS comes up as a naturally occurring solution to the problem of “how do we have stuff reference other stuff”. Unfortunately, I think there’s quite a bit of terminology mixup, and not only do different people mean different things, but also that the large part of the community attributes some things to ECS that aren’t actually ECS. Let’s try to separate things out.
[…]
Generational arena is basically just an array, except instead of having our id be an index, it’s a tuple of (index, generation). The array itself then stores tuples of (generation, value)
[…]
The key point being, this allows a language like Rust to completely side-step the borrow checker and allow us to do “manual memory management with arenas” without actually touching any hairy pointers, and while remaining 100% safe. If there was one thing to point at that I like about Rust, it’d be this. Especially with a library like thunderdome it really feels that this is a great match, and that this data structure very well fits the language as it was intended.

Now comes the fun part. What most people attribute as benefits of ECS are for the most part benefits of generational arenas.

Found this at:
https://www.reddit.com/r/Zig/comments/1cf3dj9/leaving_rust_gamedev_after_3_years/

Maybe Zig is the better replacement for lower-level (non-GC) language, when you need that, rather than Rust or C…

1 Like

I absolutely can relate. I know I am far away of being somehow good in Rust and I am trying only a few month now, but I can show you actual working Rust code, which is in some kind funny but also somehow crazy and not satisfactory, but I assure you there is no better or shorter way:

    if ! INSTANCES.lock().unwrap().is_empty() {
        let instance = (&(INSTANCES.lock().unwrap())[0]).upgrade();
        if instance.is_some() {
            instance.unwrap().run().unwrap();
            ...

Or look at this one:

rv.string_value = CString::new(self.rows.get(row)?
        .row_elements.borrow().get(col)?
        .value_s.clone()).unwrap().into_raw();

It’s a single line! :slight_smile:
Just a cast to some kind of string from another kind of string:

data.value_s = CStr::from_ptr(new_v.string_value)
      .to_string_lossy().into_owned();

Rust is a feast of .into(), .borrow(), unwrap(), clone() … and quite some more.
Sometimes, see above, it looks ridiculous.

Of couse, I am doing it wrong, but above’s code is a single project using a single Rust library, so I have no choice. These are the code lines which do what I need to do.

Whenever I do some Julia in between, I feel so relieved to just get things done with nice and shiny and readable code. It’s a PLEASURE! (not saying that Julia can not be difficult and unreadable sometimes, especially when you do the performance game!)

4 Likes

Generational arenas and thunderdome - Rust seem cool, are there any Julia packages like this already? I am trying to build a dynamic graph with metadata, and this seems like a great way to do that with less overhead than dictionaries.

I haven’t really looked at that one or different arenas [in Rust] but there is awesome: Bumper.jl -- a quick guide to Bump allocators for ultra-high performance intermediate allocations :: JuliaCon Local Eindhoven :: pretalx

and (just working, or a reason to suspect not/abandoned?): GitHub - tonyrubak/MemoryArena.jl: Memory Arenas for Julia

This article scans with my experience. Rust is super hard. The hype around Rust makes it feel like this is your fault.

In contrast, Julia is super easy. In fact, the dumber you are with julia, (explicit loops, no type annotations) often the faster your code gets!

But there is a strange drawback. If the language is so so easy, there is a problem of fragmentation (5 packages doing the same thing, 3 written in an afternoon), a problem that the average coder is thinking less hard about code, and a problem of less skilled coders overall (as Patrick Kidger left for the googly shores of JAX, he made this last complaint).

Rust code really does tend to be bombproof, not because gc pauses are common bugs, but just because it took so many tries to get it working with the borrow checker, write out all possible Error types etc. Maybe you really do get better code, because you are forced to do it over and over, and you have to be dang smart to get it working in the first place.

Judging by the rise of rust, maybe we should put in a “feature” that forces you to code with 120% test coverage before the package can be loaded? If some people want to be abused by their compiler we shouldn’t kink shame.

7 Likes

It’s not either or, you can call Rust code from Julia if it makes sense, at least no less (un)safe or impossible than from other languages, such as C or Python (you need to provide a C API for Rust code for all other languages). There are pros and cons to doing this, in pure Julia your code is fully generic, not the code you call in Rust. Also the safe code in Rust, is still safe per se, assuming at least single-threaded, you can screw it up, overwrite its memory potentially, so all bets are off potentially without --check-bounds=yes (not really enough, if you call e.g. C code, or any other language), and for Julia (non-default) multi-threading the safety potentially also likely off.

The “Lisp curse” you imply is somewhat real, with any easy language, but hopefully some package makes it as the default in any domain. With Lisp, my understanding is that people tend to want to roll their own, not adopt code from others, and “standard code” does not evolve… I don’t see why must happen endlessly with Julia (on the contrary, we are building many state-of-the-art software), nor am I sure this is true for Clojure, as opposed to other lisps.

I don’t think so and what would that actually mean?

https://www.sqlite.org/lts.html

Aviation-grade testing → Every machine-code branch instruction is tested in both directions. Multiple times. On multiple platforms and with multiple compilers. This helps make the code robust for future migrations. The intense testing also means that new developers can make experimental enhancements to SQLite and, assuming legacy tests all pass, be reasonably sure that the enhancement does not break legacy.

You can test each function, for 100% or more, or each branch, then only up to 100% (or 200% max if you consider each way as two test, not none… and then you can multiply by platforms…).

There are also some additional loopholes, exception handling is one, it’s not obvious that’s it’s an implicit [missed] branch/test, also each time you (possibly) overflow is a potential bug, and could have been a test and branch, but likely isn’t at least i C code that SQLite is based on. Same with Julia for some (i.e. default integer) types, also non-finite/Inf float results that could be checked at the source but likely aren’t in any language (Python is an one exception: for division by zero; though the exception is not in NumPy by default). No language does this check, by default, not even Rust I believe.

I really like Rust and I really like Julia. I don’t often use Rust though because I rarely need my code to be bullet proof (so it’s almost never worth it for me to spend the time writing it in Rust). If I have a need for bullet proof code, I’m definitely going with Rust over Julia, so it could be nice to have an option to turn on Rust-style compiler checks when you need to be really sure that your Julia code can’t produce unexpected results…

3 Likes

The blog post shows that features and ecosystem make a programming language more suitable for some projects than others. Since Julia and its ecosystem are constantly evolving, it would be great if the Julia-lang website would publish a blog post about the future of Julia and its ecosystem.

There are quite a few blog posts about Julia and its ecosystem:

Is there a particular blog post that you would like to see?

I think there are some multi-year, ecosystem-wide, high-impact initiatives that deserve a little more promotion. For example, a blog post about JuliaSyntax and how this new parser opens up new opportunities in the ecosystem like TypedSyntax could be very interesting.

Blog posts comparing features between various versions of Julia could be very instructive. For example, last year Jakob wrote a blog post about how Julia’s latency has improved over the last few versions, and without a blog post like that, it would be much harder to know how much Julia has improved over the last few years.
https://viralinstruction.com/posts/latency/

I wouldn’t be surprised if some Julia packages are the most advanced in some niche of mathematics or science, so it’s a pity that such a cutting edge effort isn’t better known in the Julia community.

4 Likes