NOTE: I'm not a game dev
I've tried Rust, sometimes play with C, D, Deno/TS, Nim, Java (actually I still write lots of it) and even some more cutting-edge stuff, like Unison. While they're cool, what I want is a language with really good tooling that gets out of my way without letting me write patently dumb code (like Java lets me use any object without checking for null, when it can be null but the language just doesn't give a shit to help me).
I use Dart when I want to compile to binary executable or use Flutter, and Kotlin for stuff I think the JVM has more to offer, like a server. The two languages are just a pleasure to use, pretty similar but having completely different ecosystems (which is great, you can use the best one for the job!).
I’m not convinced that ecosystem is so important for game dev. Once you have a simple graphics library, bindings to BulletPhyiscs etc most of the code is custom simulation code with no integrations needed.
Go is simply a badly designed language where the idea of "simplicity" has been maligned and proven bad ideas likes nil/null, exceptions and such have been introduced in a seemingly modern language. One would think that decades of Java, Javascript, etc. code blowing up because of this issues would teach someone something but seems that is not always the case.
The open-source ecosystem is not as massive as Go's or the JVM's, but it's not niche either. F# runs on .NET and works with all .NET packages (C#, F#, ...). If the .NET ecosystem can work out for you, I recommend taking a closer look at F#.
F# allows for simple code, which is "functional" by default, but you're still free to write imperative, "side-effectful" code, too.
I find this pragmatic approach works extremely well in practice. Most of my code ends up in a functional style. However, once projects grow more complex, I might need to place a mutable counter or a logging call in an otherwise pure function. Sometimes, I run into cases where the most straightforward and easy to reason about solution is imperative.
If I were confined to what is often described as a "pure functional" approach, I'd have to refactor, so that these side-effects would be fully represented in the function signature.
F# ticks the enums/ADTs/pattern box but also has its own interesting features like computation expressions [0]. I would describe them as a language extension mechanism that provides a common grammar (let!, do!, ...) that extension writers can target.
Because of this, the language doesn't have await, async or any other operators for working with async (like C# or TS). There's an async {} (and task {}) computation expression which is implemented using open library methods. But nothing is preventing you from rolling your own, or extending the language with other computation expressions.
In practice, async code looks like this:
let fetchAndDownload url =
async {
let! data = downloadData url // similar to C#: var data = await downloadData(url);
let processedData = processData data
return processedData
}
I often use taskResult{}/asyncResult{}[1] which combine the above with unwrapping Result<>(Ok or Error).Metaprogramming is somewhat limited in comparison to Scala or Haskell; but still possible using various mechanisms. I find that this isn't a big issue in my work.
IDE-wise, JetBrains Rider is a breeze to work with and it has native F# support. There is also Visual Studio and VS Code with Ionide, which are better in some areas.
You can run F# in Jypiter via .NET Interactive Notebooks (now called "Polyglot Notebooks" [2]). I haven't seen this mentioned often, but this is very practical. I have a combination of Notebooks for one-off data tasks which I run from VS Code. These notebooks can even reuse code from my regular F# projects' code base. Over the past years, this has almost eliminated my usage of Python notebooks except for ML work.
[0]: https://learn.microsoft.com/en-us/dotnet/fsharp/language-ref...
[1]: https://github.com/demystifyfp/FsToolkit.ErrorHandling?tab=r...
[2]: https://marketplace.visualstudio.com/items?itemName=ms-dotne...
Maybe I should just unsafe rust and see how I go....
Yes it's easier to write trivial code in python than Rust. Yes it's harder to manage memory manually than it is to let a gc handle it. I don't see the point.
Rust is a systems programming language. It's hard to write a server in it than it is to hack something in node, but it will also be faster and more reliable. Conversely, it's easier than writing it in C++ or C, while still being more reliable. That's the whole value proposition.
I've been saying this for years. I've tried to get into Rust multiple times the past few years and one of the things I've tried was gamedev with Rust (specifically the library ggez when it was still being worked on, and a little bit of Bevy). I admittedly never got far, but I gave it a solid shot.
My experience was instantly terrible. Slow compile times and iterations, huge package downloads (my project folder was roughly 1gb for a simple 2D project), and of course Rust itself was difficult to get into with lifetimes and having to wrap and unwrap my variables constantly and getting into wrestling matches with the borrow checker.
I kept telling myself that everyone loves Rust and the community loves to rave about anything Rust-related and maybe I just don't get it, but it took some time to realize that no... It's just a terrible choice for it. I even tried to make UI with eGUI and was still miserable. Rust is a systems programming language but the community is trying to convince everyone should be used for general purpose stuff.
And my other biggest problem is that they keep painting other non-Rust things as being fundamentally flawed for not being Rust. "It's not memory safe" is the biggest one thrown around, but when was the last time memory safety was actually a big problem in games? Unity uses C# which is garbage collected, Godot uses its own scripting language which makes it nigh impossible to leak memory, Unreal AFAIK has its own tools that makes memory management trivial. Rust game development feels like a solution looking for a problem to fix.
I am curious about Bevy when it becomes mature and has its own editor, but for now I'm just not convinced gamedev with Rust will ever take off.
Memory safety may or may not be important in games, but the ability of engines like Bevy to analyze system dependencies and automatically scale to multiple CPUs is a big deal. Job queuing systems have been popular in gamedev for a very long time, and Rust's insistence on explicit declaration of mutability is a big part of the reason that "just works" in Bevy.
I adore Rust because it does all the things I remember being told to do in C, but without me remembering to do them: Error codes from all functions, Ownership models, etc. But those are not good reasons for me to use it for anything I wouldn't use C for.
The same can be said for ordinary CRUD backends. Java, C#, Go and Typescript (Node, Deno or Bun) are all memory safe with good type systems and more than good enough performance. Evangelism around Rust is unfortunately still a thing. A good example is the latest hype in the community because some Google Manager said at a Rust conference that writing Rust is as fast as writing Go. Anyone having done more than a toy program in Rust and Go knows how wrong this statement is. The reasons are given in the article.
I'm not sure whether or not that's even useful in game development. I've never done any form of game development beyond some Chess game I programmed in my first year of CS 30 years ago. But I'm actually really curious as to why you've struggled with variable ownership, because I'd frankly like to improve our on-boarding processes even more for new hires.
> my other biggest problem is that they keep painting other non-Rust things as being fundamentally flawed for not being Rust
Rust has a cult and it's best not to pay too much attention to it. Don't get me wrong, we're seeing great benefit in not just using Rust over C/C++ but also replacing more and more of our C# and Python services with it, but it's a very immature language and like any other programming language it's still just a tool. If it works for you, use it, if not... Well, use something that does.
* name intentionally made to make people angry
Perhaps learn another language like Haskell, Swift, or Kotlin before Rust.
Get cargo-bloat, cargo-cache, and cargo-outdated.
Setup a memcache server and use sccache to accelerate Rust, C, and C++ compilations. It's not 100% but it's pretty awesome for things compiled at a stable build location.
Just like any platform, avoid dependencies wherever possible and use minimal crate features. Some Rust crates have an npm-like problem of dragging in zillions of dependencies.
So.... Sounds like memory safety is indeed a problem? Otherwise why do so many solutions exist for it?
Yeah, Rust definitely is not the only solution, or perhaps not even a good solution to this problem in the context of game development. But let's not pretend the problem itself doesn't exist?
Rust is not a good language for actually writing games, and the fact that it is being sold as such is really detrimental to it in my opinion, because it is holding the ecosystem back. Rust is being pushed as a language for game logic, so people try out and realize it isn't very good at that, and so they just give up on Rust in the game development industry at all and leave, understandably! If Rust were more strategically positioned, it could get a lot farther. Where it should be focusing in the games industry is on game engines, where flexibility and quick iteration and easy prototyping and being able to just reach out and directly touch and control things isn't as important, but where concerns like the clarity and maintainability of the code base, stability of the software, resource ownership and management, and eeking out every ounce of performance all become important, and so the type system and static analysis guarantees of Rust are actually useful.
This is where, I'm disappointed to say, I think things like Bevy and Amethyst have severely hurt the Rust game development ecosystem. They aren't really game engines in the traditional sense, they are more like game frameworks like Love2D except written in Rust: they force you to statically link your game code to the engine code, and write your game logic in the same language your engine is written in. This means that game developers who just want to quickly prototype game mechanics and want to be able to iterate on them in order to refine them are forced to use a language that is far too focused on correctness, safety, static verifiability, and concerns like that to actually be usable as a programming language, and worse, it forces them to compile their game logic and the entire engine together and link them together in order to build their actual game and test it, massively increasing the weight of the process and basically ruling out hot reloading or making your game independent of any specific version of the engine, or its license. It puts them between a rock and a hard place, between using some other ecosystem, or using a language that simply unsuitable for a game development.
I think the far better solution (one which I plan to very slowly feel out with my embryo engine project, which is born out of my frustration of looking at the existing rust game engines and feeling like they are all kind of lying about what they are) would be to stop with the vaporware and the hype with Bevy and Amethyst and such, and actually build a proper game engine, like they are promising to be but are not, that is its own separate pre-compiled executable that game developers don't even need to mess with at all, that picks up game assets and game code written in a more flexible, dynamic, language that's better for prototyping, and runs them, something like what Unity or Godot or even Gamebryo do. Only then will the rust game development ecosystem take off, because it will no longer be forcing a language that just isn't good for that on to people.
I've been writing a metaverse client in Rust for several years now. Works with Second Life and Open Simulator servers. Here's some video.[1] It's about 45,000 lines of safe Rust.
Notes:
* There are very few people doing serious 3D game work in Rust. There's Veloren, and my stuff, and maybe a few others. No big, popular titles. I'd expected some AAA title to be written in Rust by now. That hasn't happened, and it's probably not going to happen, for the reasons the author gives.
* He's right about the pain of refactoring and the difficulties of interconnecting different parts of the program. It's quite common for some change to require extensive plumbing work. If the client that talks to the servers needs to talk to the 2D GUI, it has to queue an event.
* The rendering situation is almost adequate, but the stack isn't finished and reliable yet. The 2D GUI systems are weak and require too much code per dialog box.
* I tend to agree about the "async contamination" problem. The "async" system is optimized for someone who needs to run a very large web server, with a huge number of clients sending in requests. I've been pushing back against it creeping into areas that don't really need it.
* I have less trouble with compile times than he does, because the metaverse client has no built-in "gameplay". A metaverse client is more like a 3D web browser than a game. All the objects and their behaviors come from the server. I can edit my part of the world from inside the live world. If the color or behavior or model of something needs to be changed, that's not something that requires a client recompile.
The people using C# and Unity on the same problem are making much faster progress.
I'm disinclined to believe that any AAA game will be written in Rust (one is free to insert "because Rust's gamedev ecosystem is immature" or "because AAA game development is increasingly conservative and risk-averse" at their discretion), yet I'm curious what led you to believe this. C++ became available in 1985, and didn't become popular for gamedev until the turn of the millenium, in the wake of Quake 3 (buoyed by the new features of C++98).
Why? Those kinds of game engines are enormous amounts of code, and there's little incentive to rewrite.
I do strongly disagree that we aren't ever going to see large-scale game development in Rust; it just takes time. Whether games adopt an engine is largely about that engine's maturity rather than anything about the language. Bevy is quite young; 0.13 doesn't even have support for animation blending yet (I landed that for 0.14).
100% this. As I say elsewhere in these threads: Rust is the language that Tokio ate. It isn't even just async viral-chain-effect, it's that on the whole crates for one async runtime are not even compatible with those of another, and so it's all really just about tokio.
Which sucks, if you're doing, y'know, systems programming or embedded (or games). Because tokio has no business in those domains.
Even there it's very problematic at scale unless you know what you're doing. async/await isn't zero cost, regardless of what people will tell you.
The main reason is that you can't ship that Rust code on PS5 in a sensible manner. People have tried, got useless toys to compile, but in the end even Embark gave up. I remember seeing something from them that they had moved Rust to server-only.
Argh I have the same issue. Sure if you write JS or Python you probably need async. My current Java back end that has like 5 concurrent users does not need async everything making 10x the complexity.
"AAA" titles are huge and/or high dev budgets. Even if a game is "starting from scratch" the engine development team are still likely taking code from previous projects to get started. Of course there are other factors. It could be a BIG RISK to move to another programming language when the team, despite frustrations, are already familiar with something else... like the perks C++ brings (you learn from trial-and-error)
Could you imagine learning Rust as-you-go... building a AAA title... and fighting the compiler? To me it is a huge risk!
That is my opinion.. but I am sure others will disagree. If there is anyone on (or did) a AAA title with Rust... I would be happy to hear more about it.
I am not saying it will never happen. Maybe a AAA title is currently in development in Rust. I honestly dont know. However, game developers... if they are looking into Rust... are also looking at Odin, Jai, or Zig. For gaming, I think they are better alternatives than Rust but (again) that is my opinion.
Now for smaller, indie games - the possibility of moving to Rust (or another language) is more likely. Likely a fair percentage have moved away from C++ now.
At one point the studio behind the Finals was writing game server code in Rust with an Unreal engine client. Not sure if that's true still
Can you please elaborate on this? I see a lot of similar concerns in other contexts too. Linux kernel's scheduler for example. Is it a throughput/latency tradeoff?
My priorities are reasonable performances and the fastest iteration time possible.
Gameplay code should be flexible, we have tons and tons of edge cases _by design_ because this is the best way to create interesting games.
Compilation time is very important, but also a flexible enough programming structure, moving things around and changing your mind about the most desirable approach several times a day is common during heavy development phases.
We almost never have specifications, almost nothing is set until the game is done.
It is a different story for game engines, renderers, physics, audio, asset loaders etc. those are much closer to system programming but this is also not where we usually spend the most time, as a professional you're supposed to either use off-the-shelf engines or already made frameworks and libraries.
Also, ECS is, IMHO, a useful pattern for some systems, but it is a pain in the butt to use with gameplay or UI code.
But this is where industry interest (the little there is) lies for Rust, is it not? This is what the AAA studios that are researching and prototyping are working on.
C++ is not a popular language to implement the actual game in for all the reasons you list. It is too slow to compile and too rigid. The people who actually build the games, make them tick, are all working in visual scripting languages.
Rust is pretty good for writing PL interpreters (and similar tooling) too, actually.
There are a few game engines[0] for CL, but most of them seem to be catered specifically to 2D games.
[0] https://github.com/CodyReichert/awesome-cl?tab=readme-ov-fil...
That's the kind of code for which Rust-like languages shine. Rich type systems make it easy to change your mind about things and make large changes to your code with confidence.
(Whether Rust tooling is actually at a level to take advantage of that is another question)
Not a game developer, but each time I tried to make one not using ECS(or something at least similar in spirit) I quickly found myself not being able to proceed due to the sheer mess in the codebase.
How does one normally avoid that?
I'd love to see a language built around ECS. I wonder how nice it can be in a language syntax where ECS is the easiest thing you can do.
I bought Mount & Blade II Bannerlord in 2020-03-30. I love it to death, but come on...
// 2024-02-01
$ curl https://www.taleworlds.com/en/News/552 | grep "Fixed a crash that" | wc -l
29
// 2023-12-21
$ curl https://www.taleworlds.com/en/News/549 | grep "Fixed a crash that" | wc -l
6
// 2023-12-14
$ curl https://www.taleworlds.com/en/News/547 | grep "Fixed a crash that" | wc -l
101
Maybe feeling like you're iterating fast isn't the same as getting to the destination faster.Edit: Lol guys calm down with the down-vote party. I was counting crashes, not bugs:
$ curl https://www.taleworlds.com/en/News/547 | grep "Fixed a bug that" | wc -l
308
Does your C++ not crash, just theirs?I'd think that an ideal game dev language would be programmer time efficient, reasonably performant and designed for skilled programmers who can handle a language filled with footguns. Basically a better version of C such as a selective subset of C++ or a Golang without garbage collection. I just don't think the kinds of security bugs you get from C/C++ "unsafe" code are that big of a deal for games but they would be for a web site or an enterprise database.
Even for database engines specifically, modern C++ is essentially as safe as Rust and significantly more ergonomic. Rust's safety features can't reason about the case when all of your runtime objects live in explicitly paged memory with indefinite lifetimes and no fixed memory address, which is the norm in database kernels. You have to write the same code to make handling these objects safe and correct in Rust that you have to write in C++. You can't use normal pointers and allocators for this even if you wanted to.
Rust's safety is designed more for normal dynamic memory applications.
> I'd think that an ideal game dev language would be programmer time efficient, reasonably performant and designed for skilled programmers who can handle a language filled with footguns. Basically a better version of C such as a selective subset of C++ or a Golang without garbage collection.
I agree so much that I've been working on this for a whole year.There is a sweet spot : non-GC, with pointers (but bounded), inference, basic OOP + tacking, and all the comforts of scripts. All in a good looking syntax without semi-colons.
So you can program fast and get a fast program.
But to go back to our subject: Rust is a great language and that’s all you need. I wish I could use it with unity.
It's not though. There's only one thing Rust is opinionated about.
> that wants everybody to program in a specific way that emphasizes memory safety above everything.
Well yes, that is literally the core proposition and purpose of the language. That's like saying java is opinionated because it wants to manage the memory.
> I just don't think the kinds of security bugs you get from C/C++ "unsafe" code are that big of a deal for games
As soon as games are networked it starts being a problem, and these days non-networked games are pretty rare.
Sounds like Common Lisp or OCaml would work well. With Ocaml I find myself being able to iterate extremely quickly because of the inferred types and extremely fast compilation times. You also have the ability to tweak the GC to your needs and the assembly is easy to read.
Lisp is well… built for interactive development
1. I am using technology in order to build this thing.
2. I am building this thing in order to use this technology.
Developers often fall in the (2) camp but don't admit it. There's an allure to using the new, sexy tech that will solve all their problems, whether that's Rust, Kubernetes, LLMs, etc.
If you're in the (1) camp, you should stick with what you know; and if you know that what you know isn't enough to build the thing, you should use whatever is most common and straightforward, not something off the beaten path.
Games seem to be the biggest trap, because solo devs often end up building a game engine when they set out to build a game. If you really want to build a game, just use Unity/Unreal/Godot, I promise it'll go better for you.
So your advice to "just use Unity/Unreal/Godot" is the opposite of your advice "you should stick with what you know" in my case. I suspect the former is good advice, and the latter is therefore wrong.
* For the sake of argument, we can pretend I only know rust. In reality I know a fair number of other languages as well, but the list doesn't happen to include C# or "random game engine specific scripting language", which seems to be the options if we're going with an established engine for big 3d games.
I think everyone in games has met an “engine person” who spends a lot of time iterating on tech, but never quite getting to the creative expression that got them in the game. I think part of it comes a bit from mythologizing breakthrough games like DOOM, where cool technology made something completely fresh. We begin to think that emulating id Software is how you make compelling art, ignoring the latter half of Kushner’s novel.
Can one always realistically do so? I suspect the underlying unspoken assumption is that one must be ideally informed about all the possible potential pitfalls and gotchas they may face when using any given technology. Aka having a very good (ideally, perfect) knowledge of the technology and its surrounding ecosystem.
It wasn't just once or twice when I've picked some very promising library or tool only to learn something isn't exactly as I hoped (or as it was advertised - docs can lie, too) after I've already spent some non-negligible time implementing something with it. Save for some teenage keyboard mashing some decades ago I'm not a game developer, but I suspect this is universal experience no matter the niche.
> Developers often fall in the (2) camp but don't admit it.
There's also a mixed approach, where people admit "I want to build this thing and use this technology, and I suspect they're a potential good match so I'm gonna try both at the same time". Any even slightly creative person must have an urge to learn and explore new things, even if they aren't exactly necessary for a task at hand. Checking on the promise, if it holds true - sometimes it does and you get a new tech you love, quite frequently it doesn't and you have a bad day.
1. An engineer solves problems, learning and using tools. 2. And artist learns and uses tools, for the sake of it.
Neither is wrong, and some times they benefit each other. I believe much of academia and research is heavy on the artist side. You just have to be clear on which one you are at any given point.
I'm not gonna use assembly for my $JOB where we need some basic web backend. That's not gonna stop me from trying on my free time tho.
It's certainly close to (1), but also a perfectly rational way to be a The Rust Way fundamentalist avoiding refcounting and unsafe who appears suspiciously (2).
The "rewrite in Rust" meme surely does not come from thin air and there is certainly some skill honing and challenge seeking involved. But perhaps a rewrite ever couple of decades isn't all that bad? And if it isn't, could there be a better time for it than "in Rust"?
It's honestly somewhat baffling to me that folks will choose Rust for gamedev right now. The state of the open sourced tools are just not there yet, especially when compared to Godot, and at the same time these games are running on PC hardware which tends to get faster every year.
Also for ECS... one thing I tended to realize is that when developing a game, pigeonholing everything into an ECS can seriously tend to get in the way. A lot of (efficiently written) game code is best handled in an infrequent event-driven way.
An ECS backed game engine like Bevy can make big mobs more efficient, but few games will actually leverage this effectively for fun gameplay and at the same time modern PCs are fast as hell.
I think about Starcraft from 1998, created when virtually all PCs only had one core, and its 200 unit per faction cap. Blizzard hasn't increased this cap because it doesn't necessarily make the game more fun. Now should a gamedev today, 26 years later, making a 2d isometric game for the PC be worried about performant multithreading????
Ah... Starcraft. It's 200 supply per player (hero units take 0 supply, zerglings are 0.5, and the supply cost goes up to 8 for battlecruisers for example). The limit is enforced when building a unit from a building. Map triggers can grant units and you can exceed the 200 supply limit.
The technical unit limit for the map was 1700, and was later in fact extended to 3400 by Blizzard. The EUD emulator (part of the official SC Remastered) allows for online custom games to be played without any third party tools on the player's part. Certain limits like sprites can be bypassed with this tool (for map makers) https://github.com/phu54321/euddraft/blob/master/plugins/unl...
EUD started out as a buffer overflow exploit which allowed custom maps to patch the game client's code. It was later fixed by blizzard but re-implemented as an emulator (with some restrictions).
These are definitely things that enhance gameplay for custom scenarios. https://youtu.be/HEv_U9WV4PA?t=1541 (yes, that is a battlecruiser shooting nukes)
I've tried to write several different types of games using it (with Bevy) in the past three years, and it just feels like shoe-horning something in.
But the biggest complaint I have with Bevy is that with all the refactoring that's been needed with the Bevy version upgrades: getting the code to compile again after the version upgrades has normally been fairly easy - but it then often didn't work correctly, and I'd have to spend time debugging the ECS system to work out what was wrong.
i.e. the "if it compiles, it'll almost certainly work" bonus of generic Rust code totally seems to fall down within Bevy.
I obviously understand that it's an in-development framework, in its early days, so some of that's on me for choosing it, but still, it's been a very painful experience, and I feel I've wasted a fairly significant amount of time over the past few years attempting it.
That small scale was exactly why the game ends up being so much about micro, and while that may make it more competitive or interesting for spectators it makes it a lot less fun to play IMO. Total Annihilation and successors were a lot more fun, and a big part of that was not having arbitrary unit caps in a way that affected gameplay; expanding the limit from 500 to 1500 did genuinely make the game more fun.
What many overlook is that using Rust has very high costs, but the edge over alternative languages is often only marginally - depending on the use case of course.
Those costs of Rust get in the way of developing the actual product. You loose speed, efficiency, but potentially gain no benefit to the users of your product.
In my opinion this is a result of big mobs having poor performance. When players get to choose they seem to like having more mobs thrown at them.
This can also be limiting for interactable objects.
But ultimately I sense the subtext here is much the same as with other Rust problems: the object oriented baby has been thrown out with the bathwater, often in the name of premature optimisation, but also with a sense of misplaced religious purity regarding the evils of state and the merits of functional programming. There never was any OOP law that your inheritance hierarchy had to be insane, or that you had to create classes for absolutely every last thing. Now we have people hitting the opposite extreme where everything has to go through the same function switched on a pattern matched enum. One of the core problems with Rust is it lacks the mechanisms to allow moving adequately out of this tarpit.
I still think Rust might have a place at the lowest level core where it is all about shuffling arrays of things through compute units, but for the higher level pieces it is clearly the wrong thing to be using.
To clarify, Rust isn't against state at all. Rust bends over backwards to make mutation possible, when it would have been far easier (and slower, and less usable) to have a fully-immutable language. What Rust is against is global mutable state, and an aversion to global mutable state isn't a religious position, it's a pragmatic position, because global mutable state makes concurrency (and reasoning about your code in general) completely intractable.
I think FP is a great way to program actions and agency but OOP is a great way to model the world. I like Rust's trait system because the polymorphism is based on what you want an object to do not what it is. But when you're creating models of the world it's usually really convenient and even accurate to use nested inheritance models. Maybe the original system for this is the flora/fauna taxonomy but it applies to a lot of things; like GUI elements or game models.
If this is correct, it might explain why the discourse is so polarized. Whether OOP is a blessing or a curse probably depends on whether you're using a programming language as a modelling language or as a logic/execution language.
And now, the most popular generally available game engines right now are: Unity - C++ in the engine, C# for game code Godot - C++ in the engine code, GDScript or C# for game code Unreal - C++ in the engine code, somewhat mangled C++ for game code BUT with also one of the most capable and widely used visual programming setups I have ever seen
I wouldn't be surprised if the "next great game engine" had a Rust core and some other language- I mean why not C# at this point?- for game code.
I found this to be true of C after many, many years coding in C. I noticed that the first selection of data layout stayed throughout the life of the code (with a lot of tweaks, additions, etc.). But didn't really think that much about it.
Until I started writing code in D. It was easy to change the data layout, and I did for experimenting. For example, changing a reference type to a value type, and vice versa. This was easy in D. It's just too much work in C, so it didn't happen.
The reason is simple:
p->b
v.b
To switch between a ref and a value type, you've got to search/replace the -> into ., and the . into ->, and not disturb the dots and arrows of the other types. When dealing with 100,000 lines of code, this is a non-starter.But with D, both reference and value types are used as:
p.b
v.b
making it easy to switch between the two, and also switching function parameters from values back and forth with references.On the opposite other languages want to have no hidden function call, no hidden pointer dereferencing..
Multiply that by all the C(++) coders on the planet and we have lost a billion man hours...
I thought this way when I was doing Java dev around 10 years ago. I thought it excused the singleton pattern. I was wrong!
You should always be able to construct an object by explicitly passing dependencies to it. Especially for testing.
It really is no fun if your renderer starts talking to your asset loader and timer directly.
The joke he mentioned about having 50 engines written but only 5 games certainly rings true and I don't think the language is the main problem preventing people from getting their projects done..
My unpopular programming opinion: languages aren't that interesting to me
I'm much more interested in the problem being solved and algorithms in the abstract sense
Bikeshedding, how many angels can dance on the head of a pin, and https://xkcd.com/927/ are some ways of loosely describing what you said.
https://en.m.wikipedia.org/wiki/How_many_angels_can_dance_on...
>but I still find it strange that so many people are so strongly focused on what their favourite language can do (usually better than others), instead of the project they're working on.
Yes, if they are so convinced of that, why are they not back in the office or at home, working busy as a beaver on their project, using that great programming language, the benefits of which they extol?
Smells fishy to me.
Oops.
Do people see what I did there? ;)
What I find more interesting is the parts of the article that boil down to "Rust isn't the best language for rapid development and iteration speed". And that may well be true! I've long thought that the future of Bevy is an integrated Lua scripting layer [1]. You don't even need to get into arguments about the suitability of the borrow checker: it's clear that artists and designers aren't going to be learning Rust anytime soon. I'd like to see a world in which Rust is there for the low-to-mid-level parts that need performance and reliability, and Lua is there for the high-level logic that needs fast iteration, and it's all a nicely integrated whole.
Long-term, I think this world would actually put Bevy in a better place than the existing engines. Unity forces you into C# for basically everything, which is both too low-level for non-programmers to use and too high-level for performance-critical code (unless you have a source license, which no indie developer has). Unreal requires C++, which is even more difficult than Rust (IMO), or Blueprints, which as a visual programming language is way too high-level for anything but the simplest logic. Godot favors GDScript, which is idiosyncratic for questionable gain. I think Rust and Lua (or something similar) would put Bevy in a Goldilocks spot of having two languages that cover all the low-, mid-, and high-level needs well.
As for the other parts of the article, I disagree with the ECS criticism; ECS has some downsides, but the upsides outweigh the downsides in my view. I do agree that Bevy not having an official editor is an ongoing problem that desperately needs fixing. Personally, I would have prioritized the editor way higher earlier in Bevy's development. There is space_editor [2] now, which is something.
And no matter how many upsides ECS can have, being forced to use it everywhere, rather when You want to, is the painfully part.
That’s what’s holding me back from jumping into Bevy.
I actually think Rust is really hard, but I also think it would be beneficial to my career.
I feel like this is the core of the author's frustration.
Rust is a systems language. It's for writing tight fast C-like code but safely and with a much more powerful type system.
The facilities you need to do this are somewhat at odds with what you want for rapid iteration.
Seems like Rust was the wrong tool for the job.
Sure, but the problem is when you have a community around that tool that insists otherwise.
I've not experienced this. Do you have examples of the rust community flaming someone for having negative opinions about the language?
It's some variation of "People who like this language can't handle criticism/are part of a cult/etc." The idea being that this will preclude anyone from responding to a criticism, because that would confirm the comment.
[0] https://www.logicallyfallacious.com/logicalfallacies/Poisoni...
So it turn out that even if he's opinion on Rust is correct, he would still have been much more productive using it than trying to build his own language for a decade…
(But he already shipped his masterpiece and he's a millionaire so he gets to chose his full time hobby as he wishes)
I have a simple question that maybe someone smarter than me can answer confidently:
If I want to build something akin to Dwarf Fortress (in terms of simulation complexity) as a browser-first experience - what stack should I choose?
Originally, I prototyped something out using React, PixiJS, and ReactPixi (https://github.com/MeoMix/antfarm). The two main issues I ran into were the performance of React's reconciler processing tens of thousands of entities when most weren't changing (despite heavy memoization) and GC lurching due to excess object allocations. My takeaway was that if I wanted to continue writing in JS/TS that I would need to write non-idiomatic code to avoid excess allocations and abandon React. This approach would result in me effectively creating my own engine to manage state.
I decided to not go that direction. I chose Rust because no GC is a language feature (especially good since GCs in WASM are heavy) and I chose Bevy because it seemed like a fairly structured way to mutate a large amount of code.
Progress has been slow for a lot of the reasons listed in this article. I've written a lot of this off to WASM being a new frontier for game dev, and I'm new to Rust/Bevy/ECS/gamedev, and rationalized my effort by noting there's not a lot of complex simulation games running in browser (that I'm aware of).
It's not clear to me that I've made the right decision, and just need to take the good with the bad, in order to develop the type of game I want in the type of environment I want.
I'd suggest Haxe with OpenFL or HaxeFlixel.
The two that I know of are Darklang [0] and Roc [1] which aim to let you compile code with type errors for the same reason you suggest.
[0] "Dark is designed for continuous delivery. As such, we don’t like requiring you to make large scale changes across your program, like changing a type everywhere. Instead, we want you to quickly discover that bad ideas won’t work, without first requiring you to propagate the type changes throughout your program."
https://blog.darklang.com/real-problems-with-functional-lang...
[1] "If you like, you can run a program that has compile-time errors like this. (If the program reaches the error at runtime, it will crash.)"
I don't get it, what's the point of type checking if not to reject invalid programs? The point of a type system isn't simply to add annotations to a programmer (and some type systems can omit them entirely) but to define the subset of programs that are correct within the set of all the programs that can be expressed.
I understand (and have used in production) optionally/gradually typed languages, and without fail codebases will opt for using types up front and not ignoring type check failure because they are always incorrect.
A type error is the compiler/run time telling you "I don't understand what you told me" so why do you want to ignore that?
And if the point is that you want to be able to change the type signature of something without having to refactor giant chunks of code, then that suggests your code is structured poorly to begin with. It should be easy to pull in what you need and play with it without affecting everything else if you haven't done a bad job of architecting the codebase.
for (int i = 0; i < n; i++)
And when I'm writing backends, usually there will be no types anywhere except at the API layer, i.e. OpenAPI spec or protocol buffers. Not once have I encountered a bug related to accidental wrong types that wasn't caught at this outermost layer.This sums it up for me:
> Rust as both language and community is so preoccupied with avoiding problems at all cost that it completely loses sight of what matters, delivering an experience that is so good that whatever problems are there aren't really important. This doesn't mean "ship crap games", it means focusing on the game being a good game, not on the code being good code.
I think this can be easily extrapolated to projects outside of game development as well.
User experience is ultimately all that matters. If you're in prototyping stages of whatever it is you're building, and games spend a lot of time in this phase, then your focus should always be on testing what the user experience will be like, rather than absolute code correctness, maintainability, and everything else that makes a long-term project successful.
The fact Rust seemingly can't deliver this rapid prototyping workflow should be a large factor when deciding which language to use.
I've been using Go as my main language for the better part of a decade now, and I think it strikes the perfect balance of code quality and rapid prototyping. It's far from the side of absolute freedom of a language like Python, which becomes a nightmare to work with after the prototyping phase is over (though this might have improved in the past few years), but it's also far from languages like Rust, and allows me to be very productive, very quickly, while also being easy to pick up for newcomers. I probably wouldn't pick it for GUI or game development either, though, but for things like CLI, network and web tooling, it's perfect.
Prototyping is certainly necessary but it shouldn't be at the cost of runtime performance – at least not too much –, because it will typically be very difficult to improve performance after the fact, which web development frameworks, and in particular shitty "web" applications like MS Teams are a testament to.
As always, it's about balance.
It should be but current state of web will show you that it is often not.
I definitely think that's a great feature. I want to learn on day 2 that the design is a dead end, not on day 101 when I ship on day 100 and there was a race condition on day 2 I never noticed.
But the thing about gamedev (I guess - I'm not a game developer) is that the code being great and doing what you hope it will do isn't 100% of the job as it is in other disciplines. In gamedev you may want to run the code, and the way it runs (fun, feel, whatever) might be bad even though it compiles, works according to a spec and so on. So while I'm usually happy to write code for a week and never run it - game development feels like it's all about the iteration.
That said, game development is also game engine development. And Rust seems absolutely perfect for engine development (you need "fearless" concurrency and performance and there are zero mainstream languages that will do that other than rust). For people who feel it's too rigid or hard to iterate with perhaps hybrid could work. Like Rust + Lua or something sounds like it could be worth trying.
To that end, whatever can help you explore the game play the fastest is what you want.
not necessarily. Loads of games and game developers do not engage in any engine development at all, they just use an off the shelf engine and make their game, treating engine developing about as close as web devs treat database development.
The latter part of this is true for any strongly statically typed language (with Rust expanding this to lifetimes), which negates the beginning of this paragraph -- once you get things compiled, you won't need to refactor, unless you are changing major parts of your interfaces. There are plenty of languages that do not have this problem because it is a design choice, hardly something Rust can "fix", it's what makes it Rust.
The borrow checker is an entirely different beast. People forget that safe Rust allows a subset of programs. Finding the subset which does what you want can range from easy, to hair-pullingly gnarly, to provably impossible.
You know, I think this point is important to get right: there are generally docs, Rust does a very good job of making it easy to write docs.
What doesn't always exist are guides that explain how to piece things together. Sometimes you wind up needing to really know the inner platform to piece together things in Rust, and while I love the language, this is one area where the community could improve.
First you gotta get OpenGL going, with its horrible stateful API, give up on it and go to DirectX9. Do a complete rewrite when DirectX10 comes out. Get your real-time lighting happening with shadow volumes, run into patent issues and get strong-armed into putting Creative sound into your game. Cycle between GLSL, HLSL, and Cg. End up switching to shadow mapping anyway. Drop Linux and Mac support. Start over with Vulkan/Metal.
I don't think Rust needs to relive most of that.
Which will never happen
I've never before been so condescended to as when attempting to ask questions there. Their lack of care for perf also drives me up a wall. Anytime they propose adding an extra layer of indirection to get around the borrow checker, I have to explain yet again that with the way modern CPUs work, extra layers of indirection have serious cache-related perf costs. Then I get told that I am yet again doing it wrong, computers are fast enough, and I am worrying about the wrong thing.
Could you please give an indication of which venues you've encountered this kind of condescension in? I don't tend to see this in the spaces I frequent, but I know it is happening, and I wish it weren't. We try, sometimes, to provide some official messaging discouraging this kind of condescension, but perhaps there's something more we can do.
Rust is great from lots of stuff but game development or building UIs isn’t among that (yet).
Other than the absurd license changing shenanigans they tried to shove through recently. Hopefully they learned their lesson.
Huh. Not games like Cyberpunk 2077 and it's good that they are not.
https://www.youtube.com/watch?v=72y2EC5fkcE
Really sells the value of having a tight developer feedback loop: it shows hot reloading for code and graphics, a reversible debugger, live profiling with flame graphs, a data inspector with data breakpoints, time travel inspection with a scrub bar, session sharing and replay with the same scrub bar and direct links from the call stack to a breakpoint, and more.
Above the many niggles they had with Rust itself, this greatly helps me understand why Rust left them wanting more from their working environment. They say they've switched back to Unity with https://hotreload.net/ to try to capture some of that, and now I see why. (It's a shame that hot reloading tooling in Rust wasn't ready for them yet, but I see why they've moved on instead of waiting/contributing.)
Does anyone knowledgeable here have a sense for whether there are any insurmountable roadblocks to bringing hot reload to Godot?
I know, because I've tried it. Once. I would #never* recommend Rust to game developers, especially not indie ones. In fact I'd recommend against it strenuously!
And this is precisely because Rust is explicitly and knowingly focused on correctness, safety, perfectly clean code, etc at the cost of iteration speed and flexibility and dynamicism, and that's bad for designing game mechanics and even just getting a game done — games have an inherently short life span and development cycle, so safety and correctness and code quality don't matter a whole lot. It's okay if they crash, etc, as long as they work enough to play. It's okay if the code is ugly, you probably won't be working on it for very long. This is even moreso the case, as the author says, because in writing a game you really want to be able to iterate quickly and just. Do shit as an experiment, even if it's temporary, to see how it feels.
On the other hand, who I would recommend Rust to is the people writing game engines, where you really will probably be working on that code for years to come, where stability and correctness is pretty important, and so where Rust's strengths will really shine — but crucially, even then, I'd tell them to make it a real engine, not a game framework like Bevy, by adding a highly flexible, dynamic scripting language like Lua or even C#, and a data format for specifying scenes and entities, and an editor. That way you don't write your game in Rust at all!
I'm disabled so I don't have a lot of energy to work on it often, but, especially once I nail down the last few design issues, I'd really love help, or even just a few eyes on the project to encourage me ;)
I only make some comments from my professional (audio) perspective:
We need the highlight author's affirmation of cli. Rust's tui (ratatui) is great. I used it to make Glicol-cli [1]. If you are a Linux user, you are welcome to test the music production of the code.
Speaking of game audio, I actually think rust is perfect for audio. I have also continued to develop Glicol [2] recently, and my recent goal (starting tomorrow) is the bevy_glicol plug-in. I want to solve bevy's audio problem on the browser.
All in all, even though I've had my share of pain with ecs, I still think rust is very valuable for game and app development, maybe not multiplayer AAA, maybe practical apps.
"... 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."
I have thought it was just me for a long time, but many of the popular styles of programming that we push definitely seem to require constant refactors in the pursuit of a solution. And I definitely see more tire spinning for the sake of the build than I do for whatever it was folks were building.
Great quote.
I think multiple refactors in pursuit of a solution can be a good thing. As your thinking/design evolves, so does the shape of the solution.
The main problem, I think, is that this goes against specifically gamedev. In gamedev whether the solution is the best or more resilient is secondary to extremely fast iteration and delivering something. Like the author says, sometimes "clunky but good enough for now" is what you need to get it over with (for the moment) an iterate rapidly over the gameplay elements you should spend most of your time on. Gameplay, not correctness or reliability or maintainability, is the most important thing in a game.
It's a different task to re-implement an already-designed language rather than designing and implementing at the same time. Nevertheless I have run into a number of the difficulties mentioned in the article, and arrived at my own solutions - foremost passing around global UUIDs rather than actual `&` references, and enforcing existence constraints at runtime.
I've experienced the protracted pain of major refactors when assumptions baked into my data model proved false.
In some regards these refactors wore some of the shine off of Rust for me as well. BUT I'm still glad the game is implemented in Rust, exactly because of Rust's dual emphasis on safety and performance.
The AI I'm developing requires generation of massive quantities of self-play data. That the engine is as fast as it is helps greatly.
Rust's strength in ML means my AI training and game code can share important types, ensuring consistency.
The effectiveness of Rust for writing CLI tools (mentioned in the article) has lent itself to a number of game-specific command-line interfaces that are of high quality.
Rust's memory safety became critical once I decided to network the game. I don't want `umpired` to be any more exploitable than it needs to be.
My constraints have been very different than the OP's; obviously it makes sense for their studio given their experience to move away from Rust. But I think Rust still has a place in games.
* https://en.wikipedia.org/wiki/Empire:_Wargame_of_the_Century * https://github.com/joshhansen/Umpire
Most of ML frameworks that I know are implemented in Python and C++. I tried looking at ML in Rust a few years ago and didn't find anything useful. Has it changed?
I think multiplayer game devs are sleeping on Elixir! It has made the network side of things so much easier and intuitive with fast prototyping and built in monitoring - so many lifetime issues are easily found and addressed. I'm pairing Elixir with Godot, Godot is used for the frontend game client. And its crazy because I thought the game client part would be the "hard" part as it would be a new skillset to learn, but Godot makes the actual game part very easy. GDScript is easy to learn, and the way Godot uses "signals" is very similar to the backend code in Elixir with message passing so its not a huge cognitive shift to switch between server/client code.
I get that BEAM doesn't lend well to highly computational tasks, but I just don't see how thats an issue for many types of multiplayer games? If you aren't making some crazy simulation game, then most of the backend computation is more around managing client state and doing "accounting" every tick as inputs are processed. The most computational task I've had is server-side NPC Pathfinding, which I was able to quickly offload onto seperate processes that chug along at their own rhythm.
OP only appears to release their games on PC so it's not a concern for them, but for the majority of developers not being able to fit into console toolchains would be an immediate dealbreaker. I have no first hand information but what I've heard from hanging around people who would know is that Sony insists that developers only use their official LLVM/Clang fork which is customised for their weird ABI.
It means that if you have a library A providing a trait and a library B providing a type, either A has to add optional support for B or B has to add optional support for A, or someone has to hack around that with a newtype wrapper. Usually, whichever library is less popular ends up adding optional support for the more popular library. This is, for instance, one reason why it's really really hard to write a replacement for serde: you'd have to get every crate currently providing optional serde support to provide optional support for your library as well.
In other ecosystems, you'd either add quick-and-dirty support in your application, or you'd write (and perhaps publish) an A-B crate that implements support for using A and B together. This should be possible in Rust.
Same thing about Golang "unused variable" and "unused import". So many times I just exploring a lib and trying things out with no intention to leave it as is, but no, Go forces to "write good code".
Golang falls in the camp of enabling fast iteration while also enforcing some sane basics. Letting off an unused variable/import with a warning is a recipe for insanity, anyone who has opened a badly maintained Java codebase will tell you this.
If you're asking if you should use Rust and you don't have a highly specific embedded use case, you should probably just use a language with decent RC or GC. As an additional bonus, 99.9% of the code you write in a GC language never has to be about memory at all. Business logic, clean and bare.
This is a bit of an oversimplification. If you are sloppy with "memory management" (unnecessary object lifetimes, unnecessary duplication, etc) even in a GC language, it is possible to have noticeable performance impacts. I'm not saying these languages aren't the right tool for the job, but it is not a free pass to ignore hardware limitations.
Jon Blow said that in one of his talks: Rust treats all code as production code. For most of the duration of a project, that's counterproductive, because it introduces a significant amount of unnecessary friction.
For most of a game's development, you're trying to figure out what the game's supposed to be. Only later does it crystallize. Rust doesn't recognize the non-crystalline phase, or rather explicitly rejects it as invalid.
I don't think the author disagrees here and is mostly talking about awful runtime alternatives (refcell, etc) but I just wanted to say it for balance.
By the time the Rust developer is finished with their refactoring, the C++/C#/Java/JavaScript developer has implemented many different gameplay features, played the game a bunch and tried them all out, and has a better understanding of which direction should their game be taking.
Man, slower than C++, that's pretty damning.I had professionally worked with C++ for a long time so getting comfortable with Rust wasn't too bad.
https://www.bittwiddlegames.com/ You can see a web build at https://www.bittwiddlegames.com/lambda-spellcrafting-academy...
I've had great success with Rust, but on projects where I knew exactly what I needed to build. Rust's focus on code correctness is great for maintenance of projects, where the priority is in keeping them stable and not causing regressions.
So while I'd say Rust is pretty quick for refactoring of something like a device driver, it's far away from the hot-reloaded time traveling live tinkering IDE.
My first impression is, of course, that the issue is there is no production Game or GUI framework around.
The author seems to complain mainly about the choices of frameworks and how bad or opinionated they are. I agree. Even Egui is too opinionated, but it makes sense on some level.
It is no problem to use bindings to some software written in C++. Rust was created to solve this exact problem: rewrite big projects that were written in C++, by slow mutation in Rust.
Honestly, I would add further that until the Unreal Engine uses Rust, we should not expect widespread Rust adoption. It will likely start with a company creating its own really custom game engine, the game becoming a bestseller, and it will spread iteratively over the years from there. Or maybe there will be a better option beyond Rust at that point.
This is the status quo: https://www.youtube.com/shorts/_zwKHgtQpc8 Let us be realistic.
Beyond that: One should see Rust as writing C with someone watching over you to remind you that you need to know the writer for each memory value. It picks up work off you. Or it should. If it doesn't, yes that is a problem, and we/you are doing it wrong.
But yes, if you are doing something that the borrow checker complains about, in other languages, either that semantic difference would have been hidden, or you would be paying for it later.
There, the author makes a point that he wants the code to work now. That is possible, and you can hotwire bad code in Rust, too. But I am sure that code is why we end up with games like Jedi Survivor.
There is no fundamental inability of Rust to do the things the author demands. If you want dynamic loading, use https://crates.io/crates/libloading (And you don't need to use the library). Do you want a global state? I will disagree with you, but take a look at, e.g., how the Dioxus project is doing it. Again I think that is always a terrible mistake, and people are thinking really of using an arena or a registry really.
Of course, some titles like Factorio are outliers. But for majority of games time you would spend to work with manual memory management in C or borrow checker in Rust would better be spent on other things.
I noticed this with Rust. That sometimes Rust forces you to pull some things up the call stack in order to access them. Even if the semantics of what you do is the same Rust doesn't let you have things in arbitrary places.
It's super weird and possibly annoying when you hit it for the first time but if you stop and think about it, the place where Rust forces you to put it is a really good place from architectural standpoint.
It basically prevents you from taking parts of a thing and delegating responsibility for them to some children, which seems restrictive, but it provides you with consistent structure of where to look for things that are responsible for something.
Rust is restrictive in so many subtle ways (and some obvious ones) but I haven't seen one where it leads to worse outcomes. Maybe I have too little experience.
I think bevy ui is the best example to give, it's like nobody ever did a ui framework with a pure ECS before. You can conclude either that's because it makes no sense to do that or that's because nobody has ever came up with the right way to do that. The bevy community thinks it's the latter.
It's especially concerning because they constantly talk about the editor, even though they don't even have any of the fundamental pieces for a gui framework in place. In bevy ui there is no way to create a reusable ui component, there are ways to do it but they all suck. So it's not even a matter of a lack of widgets or something, the problem is you can't even write a reusable widget, there is no foundation for a ui framework, and there isn't even a real plan to change it because nobody knows how to write a gui framework with a pure ECS.
But even if they figured that out, things like text input fields can't be properly implemented because there is no proper text rendering engine in bevy so they have to rewrite that first. Except that all rust text render/layout solutions in rust (it has to be pure rust because wasm/because it's rust) are still very experimental and immature.
It's a huge pain in the ass, people write games in rust because they want to write rust, not because they want to write games.
The relative abundance of game engines made in Rust compared to actual games is a bit of a meme, but I think there's something to it. Maybe Rust's feature set is just not the best fit for gamedev, for reasons outlined in TFA. Maybe it means that game engines built in Rust (which I do feel Rust is well suited for) should try to integrate an interpreter for some higher level language, IDK.
As for the design patterns that a complex game requires, if you are considering Rust for game dev and ecs design patterns it might be useful to check out projects that are Rust centric like https://spacetimedb.com/.
There's low hanging fruit i've been complaining about for years where Rust is protecting us from ourselves - orphan rules, global state, ... Look, we're adults, we can make decisions with tredeoffs.
Compile times are a tougher one, I understand that Rust does analysis that is more complex than many langs and i feel ungrateful to people who spend their free time improving Rust. But also i don't think the complexity justifies all of it. Make dynamic linking easier, reduce how much needs to be recompiled, compile generics to dynamic dispatch in debug builds, etc. - there's gotta be a ton of stuff that can investigated.
ECS just plain sucks. People use it because what they want at first is some way to have relationships between entities. References/pointers are the obvious solution in most langs but in rust, they're obviously out. The second option is Vec and indices but that falls apart as soon as you start removing entities. The next step up the ladder of complexity should be generational arenas but for some reason people immediately reach for the big guns - ECS. And it infests their game with two things that make gamedev a slog - dynamic typing and boilerplate.
Boilerplate is obvious to anyone who has done gamedev the "obvious" way before. What could be projectile.shooter.score += 1 is multiple lines which (depending on your particular choice of ECS) usually involve generics. You shift your focus from tweaking your game logic and tuning the experience to typing out boiletplate.
Dynamic typing means entities are no longer structs with fields where you can understand how they relate to each other at a glance but instead any component can be anywhere, entities are no longer real, refactoring always causes silent bugs.
However, by far the biggest issue is the community's handling of criticism.
There are practically no experienced gamedevs coming to Rust from other langs so there's nobody to give Rustaceans a reality check. Rust gamedevs are almost always writing their first game (or, yes, engine). And there's nothing wrong with that, i was writing my first game at one point too. But their attitude is that they chose Rust because they heard it's the best and they got invested in the language because it's hard(er) to learn and now with all this investment if they hear rust or their particular favorite engine might not be that great, it feels like wasted effort so they get emotional and defensive.
I've personally chatted with over half a dozen other gamedevs who came to rust with years of experience under their belt and a common pattern is that they avoid the rust (gamedev) community because they're beat down by the negativity heaped upon them every time they try to discuss the negatives. It doesn't matter they take every effort to offer constructive criticism, it becomes a social instead of technical topic.
I came to Rust because i care about code correctness and, well, quality (of tooling, docs, testing). And Rust delivers there on a lot of that. But i also wanted to write games of a larger scale than can be done in one person. My hope was that there'd be other people with the same values who wanna build cool games together. Instead there's a low single digit number of serious open source projects and a bunch of small one man games and a a whole lot of loud people who seem to think gamedev is about hyping up an engine like it's a sports team.
Myself, I apparently chose the wrong engines for my games in both cases. Not because they're bad technically. In fact, having 5 years of gamedev experience before Rust, i think my choices are better from a technical perspective but there's just not the critical mass to build a serious open source game around them.
It honestly feels that if you want a somewhat memory safe language for more general purpose use cases good'ol fashion Ada or maybe ziggs (or if carbon ever become a thing) seem 100% more approachable than rust for gamedev or gui.
The only way I see rust becoming dominant in gamedev/ui is by sheer brute force.
I think Rust is an amazing language for building a generic game engine, but a pretty crappy one for actually implementing a finished game.
Isn't it basically standard practice to have an engine written in a systems-level language with generality, reliability and performance as the top objectives, and the game itself written in a scripting/interpreted language that allows very quick iteration?
And it can even in many cases be a pretty horrendous home-brewed language. Despite having people without the computer-science knowledge to create a good language (and yes, inventing a new language is one of these things that leads to horrible results if you just learn on the job). This structure will still get you there easier and faster than trying to implement the whole game in a systems language.
I have a C++ game client, a C++ game server, and a shared C++ game script that is transferred to all clients, running in a RISC-V emulator. That means the script will fundamentally execute the same way on all clients, and the server. I have no idea what everyone else is doing. This is what I'm doing now, and the more fleshed out it's becoming, the more I actually like it this way. I don't think I could easily "go back" to other solutions.
The trade off is always reload ability
We have CI, we have LSP places we can put heavy checks without sacrificing our ability to hot reload fast
In some checkers you can put in your own custom checks too
When I did some research for the Piston project, I learned that there was a productivity technique called "meta parsing" which was used in late 60s to develop the first modern computer. This was before C. The language was Tree-Meta. Viewpoint Research Institute upgraded it to OMeta.
I thought OMeta was too complex, so I developed Piston-Meta, an alternative for Rust using a simple data structure: Start node, end node, text, bool and f64.
Your blog post really explained the reason why quite clearly, thus eliminating a journey that would be fraught with difficulty and seemingly with little return.
I am going to go with godot and hopefully that works out for me. Thank you again for sharing your experience.
For me, I could not see myself using rust especially for game development. Some of the points raised are my concerns, especially wanting to chuck something together (to improve later) only to fight the compiler, etc.
I would be interested to know what language they choose moving forward. It seems the contenders are likely to be:-
C - because, you can,
C++ - ditto,
D - I think it is largely ignored, and there is the betterC flag
Zig - Seems interesting,
Odin - Also interesting
Anyway... going to enjoy reading the comments, now.
Rust does try to force you to refactor sometimes, but you have the option to fight back.
I really wish the author had followed this with a list and an explanation.
Now, a language like Rust makes some aspects better because it ensures that the maintenance (refactoring) is done correctly -- reducing the risk of mistakes -- but it comes at a cost: you must explain your handling of memory (before and after the refactor) to the compiler (plus, the compiler doesn't understand all patterns). I think it's too soon to empirically compare this cost to the gain in reduced risk and determine when each option is more or less advantageous (and perhaps it is also a matter of personal programmer preference), the fact remains that maintenance of programs in all low-level languages is always more costly than maintenance of programs written in high-level languages because of the low abstraction inherent to all low-level languages.
When writing in a low-level language some may prefer the Rust approach while others may prefer less restrictive ones [2], but people choosing any low-level language should be aware of the added maintenance cost they're invariably signing up for. Sadly, this cost only becomes apparent at later stages of the project.
[1]: Some people claim that memory is just like any other resource (e.g. file descriptors), but this is incorrect. Memory and processing are fundamental to the very nature of abstract algorithms, and differences in how memory is handled change the available range of algorithms. E.g. finite state machines, queue automata, and Turing machines differ only in how memory is handled and accessed. In short -- memory and processing are special resources and are not the same as IO resources.
[2]: I'm personally not a big fan of Rust's approach -- and I particularly dislike C++'s and Rust's "zero-cost abstraction", which is the attempt to make the low abstraction ability invisible in the final code without changing its fundamental aspects -- but I recognise that people's opinions differ on this matter. I also reject the claim that there's no middle ground between Rust and C that offers an intermediate tradeoff between them, i.e. that there is no safety premium to a language that offers some of Rust's safety guarantees but not all of them, such as Zig, or offers better and effective assurance of some properties without a sound guarantee.
Anyway - no one is going to ship a game written in Rust with this attitude. To the other folks out there happily writing their games in Rust - don't be distracted! All it takes is one success story to prove the concept :)
Personally, I found using Godot with some parts in Rust via gdext quite enjoyable.
You can avoid dealing with GDScript for important parts of the code and have access to OS threads if you want them, etc. - but can also prototype features in GDScript and write the UI, etc. there for fast testing, and keep a good separation of UI and graphics presentation vs. the actual game logic.
Can someone explain the obsession with combining ECS with generational arenas?
Rust is supposed to be a better safer C/C++.
Then lot of comments here that games are best done in C++.
So why can't Rust be used for games?
What is really missing beyond an improved ecosystem of tools. All also built on Rust.
Of course, there are many people in the Rust community who are not religious and try to improve the language, but my general feeling after reading a lot about Rust is to stay very far from the church of Rust.
The best response to this type of community is humor, like this video about a Rust Senior developer https://www.youtube.com/watch?v=TGfQu0bQTKc
I'd say I only have two somewhat arbitrary comments on this piece:
> Rust on the other hand often feels like when you talk to a teenager about their preference about anything. What comes out are often very strong opinions and not a lot of nuance.
This is a rabbit hole of a topic so I don't want to go too deep into it, but this isn't a Rust-specific issue (though it may be a current Rust issue). I've seen this same pattern play out across so many languages over the years, from Lisp to Rust to everything in between.
A very unscientific and definitely not charitable way I've thought about this over the years is that programming, by nature, is an OCD person's dream. We wind up with a pretty large chunk of people who seemingly move language to language in the hype cycles in search of some weird nirvana level that is likely just unobtainable. I feel like Rust has slowly started shedding this as the community has grown/matured and some people have moved on to the next hype cycle but I often find myself wishing it'd happen faster.
I write Rust because when I sleep at night, I just don't get woken up by being paged for nearly as many weird edge cases. The Rust I write often has a litany of compromises because I want to just get shit done and move on with my life, and the remaining guarantees are still good enough. The number of times I've had to tell people to leave it be is definitely higher than it should be.
> I know that just by saying "global state" I'm immediately triggering many people who have strong opinions on this being wrong. I feel this is one of those things where the Rust community has created a really harmful and unpractical rules to put on projects/people.
This isn't really a Rust-specific thing, though I can't fault the author for including it. People have been beating the drum of "no globals" for as long as I can remember... and simultaneously, as long as I can remember, game devs have come out of the woodwork to politely explain that the programming they do is often under very different constraints.
I still periodically use global state for things because it's just faster at points, and no, I've never cared if people get annoyed by it.
Anyway, here's to hoping this leads to positives for the community.
lisp lisp lisp lisp lisp lisp lisp lisp
- The very first thing that comes to mind is why actually use Rust for gamedev. From the article it seems like the author got into Rust through Godot, but that does not explain why commit to use Rust for a full game. What was the reasoning behind picking Rust?
- It feels to me that there's a mix of criticism to Rust as a language, Rust as a community and libraries/frameworks written in Rust (in particular Bevy). Personally I think these are completely separated matters so I would like to know why the author treats them all as a unit.
- I've always got the impression that gamedevs try to have their cake and eat it too which is almost always impossible: they want to have quick iterations and write "simple code" while having low level control of everything (ex. manual memory management, usage of pointers, etc.). For example, the author mentions wanting access to methods like "play_sound()" but at the same time mentions that some patterns are unacceptable given the "overhead [...] due to memory locality". I've never heard of an ecosystem where you can have everything without any compromises.
- In particular, I get the impression that the author has a lot of troubles with ECS and instead it tries to bend it to work in a OOP fashion (for example, through the usage of "fat components" as he calls them or preferring virtual dispatch over `match` statements). He claims that he has put in a "lot of time" in trying to make it work but I get the impression that this effort was mostly wasted in trying to bend the language and libraries into something that just won't work out. It's like trying to use a circular saw to polish mechanical watch pieces: an exercise in frustration. At some point in time I'm sure he asked himself why to keep on pushing on, and I would like to know why he continued to be committed to such process.
- The author claims multiple times that they work in a single threaded environment where they should not care about concurrent access so they should not pay the price in the type system. I agree that this should be the case but then it proceeds to list examples that show a different situation. One of them is the claim that they cannot use a "god" context to pass down every dependency due to the borrow checker, listing code that tries to hold a reference to a "camera" system while passing the context to the "player" system. In particular this does not make sense because: 1) If you're in a single threaded environment you don't have two systems using the same context at the same time (because there is no "at the same time"); 2) if the "player" system does not need the camera then it won't change it, and if it does not change it then there is no need to take a reference to it earlier, you can just take it after the "player" system has finished. I know that coming up with brief examples is extremely difficult but either the example does not properly represent the reality, or the author is actually working in a multi threaded environment (maybe without actually knowing about it).
As an observation, the author mentions multiple times that Rust pushes you to write "good code" and I fundamentally disagree. "Good code" is very contextual, just like the idea of "simple code" where he checks for all collisions and plays a sound in 3 lines (is this actually "simple"?), so instead I would say that Rust forces you to write "correct code", that is, code that won't (or is unlikely to) fail at runtime. I believe this is a very important distinction that you always need to keep in mind when evaluating a tool such as a programming language.
Finally, I do believe though that Rust is a bad choice if what you're interested in is to build games quickly without consideration for performance (and you most likely don't need to care in 2d games) and their decision is very reasonable: C# and Unity are just aligned better with what the author is actually interested in doing.
I am worried because:
- Games seem like a no-go, as articulated in the article
- Web usage has been dominated by Async, with no signs of reversing. I have no interest in this. And, there is no promising Django analog or ORM.
- Emebedded support on Cortex-M, and to a lesser extend RISC-V is good at its core, but the supporting libraries have a mix of A: the game failures listed in the article (Driven by hype, mostly makers, serious companies are not using it), and B: Also being taken over by Async.
This is disappointing to me, because IMO the syntax, tooling, and general language experience of Rust is far better than C and C++.That has got to be the most "I didn't think this through" take ever.
While it's a known pain in the ass. Not having it is a bigger pain.
The moment you allow this, you have to find a way to pick between several implementation - and they don't always have sane names.
Orphan rules prevent this from happening.
I tried to like it, but I can't. It doesn't align with my way of thinking.
On the "hot reloading" remark: I believe that, to some extent, compiled languages that lean into metaprogramming are innately at odds with the concept of hot reloading. You're spitting out a (mostly) monolithic binary - rewriting that on the fly just isn't going to be reliable beyond an extremely basic level, and shoving it all into some kind of VM for the purposes of hot reloading introduces variance and general performance overhead that both mean that the "hot reload" environment is no longer an accurate depiction of the real application's behavior.
> I wasn't thinking "what's the right way to get a random generator in here" or "can I assume this being single threaded" or "am I in a nested query and what if my archetypes overlap", and I also didn't get a compiler error afterwards, and I also didn't get a runtime borrow checker crash. I used a dumb language in a dumb engine and just thought about the game the whole time I was writing the code.
and,
> The prevalence of perfectionism and obsession with "the correct way" in the Rust ecosystem often makes me feel that the language attracts people who are newer to programming, and are easily impressionable.
I see someone who simply does not think about those kinds of things when writing code. And that's completely, entirely fair. Rust is not for them, then.
But they seem to act like it's an issue with the language that it does not serve them specifically, and their way of thinking in particular, not even pertaining to game development.
Because the thing is, I don't suffer from the issue they're describing. I don't find it difficult or distracting to think of edge cases and implementation details when I am writing out a solution. In fact, I can't help it. I love Rust, because its strong typing and strict static analysis actually support and justify my way of thinking. They're not obstacles for me to overcome, certainly not like how it's described here.
When I use a language like JavaScript, people tell me that I care too much about details or that I don't need static type information because I can just assume. JavaScript is not for me, because it doesn't support my way of thinking. It is terrible and sloppy and completely unchecked. It's absolutely great for banging things out without giving a care in the world about a single implementation detail that isn't relevant to the actual problem at hand. It's terrible if you actually do care about those implementation details, because nobody else who writes JavaScript does. Everything you interact with is going to be just as shoddy as the language itself.
(I have a job writing JavaScript, so this doesn't mean that I can't use the language. It just means I do not like it. I do like TypeScript.)
So this might just be a fit issue. I haven't read the rest of the article yet, because this stuck out to me. I see some other HN comments talking about async code and GUI libraries, and those are all completely valid concerns, but in the article these valid shortcomings are seemingly mixed with what I'm going to call "neurotype issues". In other words, I suppose the author just isn't autistic enough. It has nothing to do with being new to programming or not.
And that's fine. It's just not an issue with the language that it doesn't serve you as well as it serves others. After all, Haskell is the same way. I'd say most programming languages are the same way.
1. What behavior do I want?
2. How am I implementing that behavior?
Experimenting with #1 is slowed down by the current end point of #2. Sometimes not at all, sometimes a lot, depending on luck and how #2 anticipated the class of experiment I am trying.
Rust adds:
3. How can the implementation code be organized so its stringent safety checks are validated?
Now experimenting with #1 is complicated by two dimensions of design history instead of one. And the latter dimension being two steps of abstraction/design-dependency away from concern #1, is going to be very brittle.
Never used Rust, but that sounds painful.
You just need to read another 50,000 word fasterthanlime essay. Then you'll not have problems any more.
> 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".
Not only this, but they openly mock other language communities for not drinking the koolaid.
I like Rust, and the Rust community, and fasterthanlime, for what it's worth. But I think these points raised in the article are very much valid.
That seems to be the crucial point. Rust is optimized for writing complex systems software in large teams. That’s not a great fit for a small team hacking on something that is at least in part an art project. You wouldn’t choose something like Ada for that either.
The vibe that I'm getting is that it's filled with people that don't particularly care about programming, they just want to get stuff done(TM), this is also highlighted by the fact that they are willing to write completely inadequate code just to see things working. Rust is not that, and that's a good thing.
More generally, I'd say that in gamedev anything goes, as long as it's fun and isn't too buggy. Rust is not, and never will be able to accomodate that mindset, which again, is a good thing if you think for 2 seconds and consider what Rust is actually aimed at, which is safe systems programming.
You can have the core engine written in Rust and have a scriptable language on top of it, there aren't any major pain points in this regard. The scriptable language will be able to provide all of this hot-reloading-anything-goes-yes-sir bullshit that we all know and love.
tl;dr: Use the right tool for the job. A language designed for safe systems programming can't do non-safe non-systems programming very well. Who knew!
Virtually all of the points outlined in the article stem from the above.
Rust is not good for raw performance. Neither for prototyping and iteration.
Personally I think operating systems (kernels) should be as performant as possible, and C/C++ has been good enough for decades.
Anyone really unhappy with Linux/BSD/Windows/macOS performance?
What systems are we talking about that benefits from Rust? Advanced weapon systems that should absolutely not fail? Controllers for air planes? Traffic controllers? Radar? Power grid?
Google, fb, amazon, etc. use C/C++ to squeeze the most performance out of anything I/O heavy, and security is not an issue that deep in the stack, that's not the exploitation layer.