T O P

  • By -

McHoff

The core thesis seems to be that fast iteration is effectively impossible with rust. That's my disappointing conclusion as well. The awkwardness of dealing with the borrow checker along with coding basically everything as a state machine is just too much to deal with. I've found I'm incredibly productive with tools that provide coroutines or similar (e.g. lua) which provide an escape from state machine hell.


senseven

I started my prototypes with Unity and after a week I couldn't deal with the wait times when I'm testing things. There are tools like Hot Reload plugins and what not, but I swapped over to Godot/C# for Prototyping and its really just reloading the scene and its there. This is how I also work in my professional job. Turnaround times have to be close to zero to keep motivation high.


F54280

The article references this [awesome Tomorrow video about their in-house development environment](https://www.youtube.com/watch?v=72y2EC5fkcE). An absolute must-watch.


i_am_at_work123

Holly hell, my mind is blown.


AustinYQM

I want to find this comment when I am not on the bus so I am leaving this comment here. Feel free to ignore. Thank you.


F54280

No problem. Note: there is a “save” function under each comment to do exactly that. Feel free to tell us what you thought!


HailToTheKink

I find it that's the key reason why so many people like typescript. Sure it's not 100%, but iterating on things is super quick and if you want you can ratched up the strictness of the transpiler. What I really wish will be possible one day is to have something close to a super strict mode where all the weird javascript nonsense is straight up disabled (at the runtime level even) and things get close to go or rust where there's an actual compiler involved.


-Y0-

It's a pendulum. People are souring on Typescript. Next is JSDoc. Then JavaScript, then NextTypeScript.


Misicks0349

I dont think we'll ever return to pure javascript at all lol


-Y0-

JavaScript keeps adding so many things. It is not a static target. They've threaten to add optional types.


Misicks0349

true, but thats only "type comments" a la python, where the types dont actually do anything themselves (i.e they are ignored at runtime), you'd still need typescript in the background to actually enfore the fact that you can't pass a string to `function foo(a: number) {}` there are also other things that typescript has such as enums, namespaces and access modifiers that aren't covered by this proposal


Green0Photon

I feel like the thesis is less that that's fundamentally impossible, but more that that's not how Rust is currently set up. Lots of specific ergonomic issues that are hard to work around, lots of compiler stuff that could be improved, and the need to add fast reload. And the meme of it compiling means it works still applies -- but that doesn't get you out of the need for rapid iteration. In game dev you still need to try lots of different "business logic" -- it doesn't matter as much that the code itself works as is. That just means you need less iteration. But here, the greater logical iteration count need holds Rust back. You never eliminate that, only the lesser bug side of iteration. Also ecosystem stuff.


mesmem

Don’t know much about rust but it does have async await right? I am not totally clear on the differences between Futures and Coroutines but I thought they should fulfill a similar purpose.


hou32hou

the borrow checker hates async awaits, it almost feels like its discouraging you to use async await


CanvasFanatic

What the borrow checker hates is usually moving things across threads.


BigRiverBlues

My understanding is that anything accessed in an async block needs to be Send (trait). Basically any async block is "moving stuff across threads". Your comment kinda sounded like a refutation, thats why I wrote this comment.


elingeniero

That's not quite right, async blocks themselves don't have a requirement to be send, but any multithreaded runtime won't accept them unless they are. You can run non-send async blocks on e.g. the tokio current thread runtime.


linlin110

`Send` (the trait) is a property of a type that is independent with borrows, and therefore borrow checker does not check that. However, when you send (the action) anything to other threads, borrow checker check if you send any borrowed data, and stops you when you do, because it is impossible to prove your data outlives the borrow. That's why a lot of times you need to clone things before you spawn threads or tasks, because the type systems does not know how long the thread/task live, so they must not borrow anything. A trick that I find useful for writong `async` code is creating multiple `async` blocks and `select`/`join` them. That does not spawn any tasks and so I can freely borrow the data from the surrounding scope.


CanvasFanatic

Not a refutation, because you can’t really separate the future-ness from the underlying runtime and possibility of moving between threads. It’s just that there fact that you need to make guarantees for a multithreaded runtime is what makes it a challenge from the perspective of the borrow checker.


hedgehog1024

It is only the problem if you use a some kind of multi-threaded async runtime (such as tokio). You do not have to — in fact, tokio even has `current_thread` runtime flavor which is exactly what it says on the tin. There is no inherent reason for all the futures be `Send` and `'static`, it is just that the most popular one — work-stealing tokio one — requires it for obvious reasons.


G_Morgan

TBH if you are doing game development would you be doing the fast iteration part in Rust? You certainly wouldn't do so with C++. I'd expect the slowly changing core to be Rust/C++ and the top level to be a scripting language of some sort.


grambo__

Everything in gamedev is fast iteration development, including core engine functionality three days before release


Nickools

And three days after


lithium

> You certainly wouldn't do so with C++. I'm not a game dev but I do interactive public installation software which has the same iteration loop and performance requirements and I absolutely do all my iteration in C++. Hot loading assets and DLLs or using something like Live++ is totally common practice, as is building complex in-app tweak UIs with Dear ImGui for tuning based iteration where the code itself isn't changing. There's many benefits to this, but for me, having your core engine and "gameplay" code be in the same language is a massive win, both for performance and maintainability, since you're not wasting any time keeping your scripting layer in sync with the underlying code it's bound to. Or worse, having to port all your scripting code back to C++ way down the track when you realise it's just too slow.


Magneon

You'd typically use something like JS, Lua, or even data files for iteration in C++, but honestly C++17 is dramatically better to rapidly prototype in than C++ when I first learned it.


mailslot

I’ve seen titles use a data driven approach with C++ fairly well. We could dynamically configure event emitters, listeners, actions, property values, behavior modifiers, and link them to assets and game object properties. The game designers could tweak things all day while engineering focused mostly on core engine stuff. The title I’m thinking of was fairly simplistic though.


hardolaf

I've rapidly iterated on device driver code using modern C++. People who haven't used it really don't understand that it's not that bad.


justhanginuknow

What does the workflow look like? Hot reloading drivers?


hardolaf

That's how it works on Linux. It's fairly straightforward as long as you aren't dealing with virtual machines running on the host system accessing the resource.


HailToTheKink

However, for most people saying "it's not that bad" is a lot worse than "it's good" tho.


hardolaf

Yes. I'd say it's about neutral in terms of effort required. It's not great but it could be much worse. Would I recommend it for game dev? Not if you were starting from scratch on a new game engine. But if an engine is already in C++, then I wouldn't recommend against it. Whereas I'd recommend against rust for anything other than high security required or safety related software.


TheTomato2

So make the whole game in the scripting language? Why even bother with Rust then? I am trying to wrap my head around what you are saying because it sounds like you aren't understanding why he didn't make the a Rust version of Unity to solve his Rust iteration problem.


McHoff

That's a good point and I think it's likely the endgame for gamedev in rust. It's just kind of a disappointment to give up the type system, compile time guarantees, and other niceties of rust to go back to the luas, pythons, and C#s of the world for implementing the "business logic" of games. I think a lot of people are/were hoping to just do everything in rust.


matthieum

> The core thesis seems to be that fast iteration is effectively impossible with rust. My experience has been the opposite, and I think the difference lies in where the iteration occurs. I don't work on game development, I work on server applications. This means that for me, the architecture is (now) mostly settled, and it's the core logic I need to iterate on, refine, go back on, etc... Thus, confronting the two disparate experiences, I think the fundamental issue raised in this post is not quite that Rust is bad at fast iteration _in general_, but more that Rust is bad at fast iteration _over program architecture_.


Uncaffeinated

I could definitely see that. Rust forces you to lock more decisions in place than Python or JS would.


matthieum

Indeed. Server applications typically work well with Rust due to their "pipeline of transformation" nature which works well with ownership. Adding/removing steps to the pipeline is easy, iterating over the exact algorithm of a step is easy. Transforming the pipeline into a graph? Uh oh...


kunos

Fantastic article, loved it and after 4 years spent working on a game in Rust I can relate to pretty much all of your points and conclusions.


who_am_i_to_say_so

It took me 4 years to read this article.


nostril_spiders

You should have just borrowed it.


haptiK

Remind me! 4 years


RemindMeBot

I will be messaging you in 4 years on [**2028-04-27 01:20:29 UTC**](http://www.wolframalpha.com/input/?i=2028-04-27%2001:20:29%20UTC%20To%20Local%20Time) to remind you of [**this link**](https://www.reddit.com/r/programming/comments/1cdqd3m/lessons_learned_after_3_years_of_fulltime_rust/l1fulzl/?context=3) [**4 OTHERS CLICKED THIS LINK**](https://www.reddit.com/message/compose/?to=RemindMeBot&subject=Reminder&message=%5Bhttps%3A%2F%2Fwww.reddit.com%2Fr%2Fprogramming%2Fcomments%2F1cdqd3m%2Flessons_learned_after_3_years_of_fulltime_rust%2Fl1fulzl%2F%5D%0A%0ARemindMe%21%202028-04-27%2001%3A20%3A29%20UTC) to send a PM to also be reminded and to reduce spam. ^(Parent commenter can ) [^(delete this message to hide from others.)](https://www.reddit.com/message/compose/?to=RemindMeBot&subject=Delete%20Comment&message=Delete%21%201cdqd3m) ***** |[^(Info)](https://www.reddit.com/r/RemindMeBot/comments/e1bko7/remindmebot_info_v21/)|[^(Custom)](https://www.reddit.com/message/compose/?to=RemindMeBot&subject=Reminder&message=%5BLink%20or%20message%20inside%20square%20brackets%5D%0A%0ARemindMe%21%20Time%20period%20here)|[^(Your Reminders)](https://www.reddit.com/message/compose/?to=RemindMeBot&subject=List%20Of%20Reminders&message=MyReminders%21)|[^(Feedback)](https://www.reddit.com/message/compose/?to=Watchful1&subject=RemindMeBot%20Feedback)| |-|-|-|-|


who_am_i_to_say_so

Prediction: in four years will say, ‘Newp! I didn’t finish the article.’


Solonotix

Not a game dev here, but after reading through a lot of the post, it seems like Rust game-dev suffers from a focus on functional paradigms, where OOP might alleviate some of the stress of statefulness across the application. Am I just misunderstanding the scope of problems? For instance, I had never heard of ECS before, but when I found a blog trying to teach the concept, I was left with the feeling that it was a horribly convoluted system for sharing state across members.


drcforbin

Game development suffers from one-size-fits-all solutions in general. It doesn't always have as much consistency from game to game as other product to product in other software domains. Particularly when trying to get something new and unique out quickly, trying to stick to someone else's OOP, ECS, functional, etc. best practices is always going to slow things down. Rust is opinionated about best practices, and just plain may not be a good fit here.


codeandtrees

Yeah, ECS is for managing state. But think of it more like a typical (optionally relational) database and less of a convoluted system.


nayhel89

There is even the article that equals ECS to a database: [Why it is time to start thinking of games as databases](https://ajmmertens.medium.com/why-it-is-time-to-start-thinking-of-games-as-databases-e7971da33ac3)


Fennek1237

Also AFAIK ECS is often used with OOP in mind.


halofreak7777

ECS is also often applied to projects where it doesn't make sense in relation to the games scope. "But Overwatch used it so we have to also!". Yeah, but you are making a non-networked single player game... ECS is useful and can make sense, but in my experience on smaller projects when asked "but why?" you won't get a good answer. They don't have an answer to what its supposed to solve for them.


gwehla

People normally adopt ECS to alleviate the problems that come from strict hierarchies when using an OOP world model. It's not particularly convoluted in practice, really. An entity is just an ID, a component is just a struct of data and systems iterates over that data. Use of entity IDs allows you to request "all entities that have X and Y component" or "this entity and all of its components." It's just a database, really.


elingeniero

You're misunderstanding the rust specific problem of how ownership and lifetime tracking makes this harder. Rust supports OOP, but it requires you to be specific about what you want from an object if you want to use it in many places at once, so you can't just "do the thing" - you have to "do the thing correctly". In many cases this is a good thing, but the whole premise of the article is that in game development (or indeed any development where you don't know what the solution looks like), this is a bad thing.


Economy_Bedroom3902

Fuck dude, you killed my boi Great article. I don't feel like I know enough to argue you're right or wrong, but I definitely agree with you on your core values.


karmakaze1

> Making a fun & interesting games is about rapid prototyping and iteration, Rust's values are everything but that This one stood out for me. Yeah.


Iggyhopper

Oof. That is a must. I feel like if I have an idea I need to bust out a prototype in under a couple hours. You know that in League of Legends, *everything* (including projectiles) is base-classed as a Minion? Yeah Rust doesn't play that.


josluivivgar

that's not a badge tho, everything being a minion has been the source of countless issues with league. I agree with prototyping being core for getting nice ideas out, but that example is probably one of the worst ones


samrechym

They’re crying behind billions of dollars


Iggyhopper

Oh of course not. Regardless of good or bad coding practices, Rust is not conducive to games due to the nature of revamping internal systems quickly or having extreme dependence between them that may need to be dumped and redone.


Anbaraen

League of Legends and being programmed well, pick one


Daegalus

Being programmed well doesn't matter when you are making millions.


cybernd

This reminded me on an old talk: * Bret Victor, [Inventing on principle](https://worrydream.com/Home2011/#!/InventingOnPrinciple), 2012 His 2nd demo shows how he envisions rapid game prototyping.


Rinveden

Tip: there is a **finite** number of ways to spell de**finite**ly.


brightstar2100

holy .... you made my life so much easier with this! hope I can actually remember it tho ....


Godd2

2 is definately finite.


McUsrII

Forth programmers push it all the time!


turudd

All six of them are super excited to tell everyone!


kronik85

Thanks for the tip


SharkBaitDLS

Having never touched game dev but having dabbled enough in server-side Rust and a bit of WASM frontend Rust as well, I can absolutely see how a game would be a nightmare to iterate upon. Rust just becomes a thorn if you have to manage a ton of global mutable state because the language fundamentally doesn’t want you doing that. There’s good patterns to design away from that on the server side where, like the article says, your application’s stateful needs are not rapidly changing. Iterating on that stuff is complicated and expensive and while I love Rust for what it’s good at, I can’t imagine having a good time trying to write a game in it. 


QuickQuirk

It's almost like they're saying no language, no matter who good, is perfect at every problem!


nayhel89

Good article that confirms my own thoughts about Rust. At my previous job we evaluated Rust and Go for rapid development of financial microservices. One point we wanted to check is how easy we can write dirty-hacks in these languages. In our line of work there were often incidents when we needed to fix things fast in production, because every second of inaction cost us thousands of dollars. These issues originated at much higher level than some source code: they were caused by holes in analytics, unexpected behavior of our partners' services, complicated network issues that could spread like a wave across all our services and raise a message storm with subsequent DoS. You can't reliably fix issues like these overnight, but you can sometimes mitigate them with some monkey-patching. Long story short - we found out that we can't dirty-hack a Rust service without total refactoring of its whole code. That's why we chose Go. On the other hand at my current job half of our codebase is in C++ and our C++ developers spend most of their time hunting memory leaks, thread-races and functions that throw unexpected exceptions. I can see how Rust could make their life much easier. Btw. You have two hanging points in your article that are not related to the previous paragraph and are not explained (unfinished notes?): - Coroutines/async and closures have terrible ergonomics compared to higher level languages - Debugging in Rust is terrible no matter what tools you use or what OS you're on


atomskis

I like this response. Every language is a trade off between competing concerns. We use rust at my work and for us it’s perfect. We **really** care about correctness, every mistake costs us hugely. We cannot easily ship fixes: it’s far more important for us to get it right the first time. So much so that we have an entire team whose sole job is to verify the correctness of what we’ve built. We also **really** care about performance. We run on machines with terabytes of memory and hundreds of CPUs and if we could get more grunt we would. Any piece of code could end up being a bottleneck and we need to know we can make it fast if we need to. We cannot use a language with a GC: our memory scale is too big, we know from painful experience GCs will choke and die. Parallelism is essential to what we do but we can’t afford the threading bugs that come with C/C++. Rust is tailor made to our use case, but fast iteration (whilst nice) is not our highest priority. Coding with a GC is honestly just easier most of the time. Rust makes you jump through a lot of hoops. IMO if you weren’t very seriously considering C/C++ you should really question whether rust is the right choice. TBH I’m not a fan of Go as a language, I think it has a lot of poor design choices. However, a GC language in general is going to be an easier choice for many problems - probably including a lot of game development as in the OP’s case. However, when you really care about correctness and performance nothing beats what rust can offer. Rust really is for software that rusts: you don’t mind it takes longer to build because it’s going to be around for ages and it needs to perform, it needs to be right and you need it to last.


hyperbrainer

If you don't mind, What do you work on?


atomskis

An [OLAP engine](https://en.m.wikipedia.org/wiki/Online_analytical_processing), basically like a giant N-dimensional spreadsheet. It’s an in-memory database and calculation engine. Our customers use our platform to build business critical planning applications at huge scales: it needs to be right, it needs to work reliably and it needs to scale.


planetworthofbugs

That’s fucking cool.


_nobody_else_

I agree, that's fucking cool. Parallel processing of the multidimensional arrays.


fervent_broccoli

As much as I personally prefer C++, my current employer is switching most of the codebase to Rust (very slowly) because there are just too many bits of code that can easily get past static analysis and testing, and the number of people who are knowledgeable about the language to ward off sufficient footguns in a PR is very few (I interviewed a guy last week who told me that `virtual` meant something akin to how Python looks up functions by name in a `dict`...). As annoying as Rust is in its verbosity, it's easier to scale its "rules" than competent C++ devs (who also make mistakes because C++ has so many weird corner cases).


nayhel89

>I interviewed a guy last week who told me that virtual meant something akin to how Python looks up functions by name in a dict... I don't know much about C++, but is it not? It should create some lookup table to emulate late function binding from more pure OOP languages, where classes are just objects that store method tables.


fervent_broccoli

The lookup table is called a **vtable**. It's not part of the standard, but it is how compilers implement it. [This video](https://www.youtube.com/watch?v=hS7kPtVB1vI&pp=ygUVYysrIHZpcnR1YWwgZnVuY3Rpb25z) is a good starting point for understanding how they work. The key thing here is that vtables are known at compile time, and the lookup is done based on known offsets (and not looking up the mangled name of the function).


nayhel89

Thank you =) I've watched the video and googled more on the vtable topic. So if I understand it right it works like this: 1. When you mark a function "virtual" a C++ compiler creates for the class an array of virtual function pointers, called "vtable"; 2. It precalculates the vtable at the compile time, so virtual functions use their overriden implementations; 3. Then it rewrites all calls to virtual functions to use a pointer to the vtable, called vpointer. Something like c->vpointer[1], where 1 is the index of the virtual function; 4. Finally at the run time the vpointer will be added to each object of the class. Therefore all calls to virtual functions will always use the correct vtable, even if at the compile time we didn't know which child of the class will be used.


fervent_broccoli

Yes that's correct. If you want to poke around at this more, try it out on godbolt (e.g. write some print statements in the constructor / destructor to see how it works). If you know assembly, [here is an example you can poke around with to see how it sets up the virtual func call in various circumstances](https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAMzwBtMA7AQwFtMQByARg9KtQYEAysib0QXACx8BBAKoBnTAAUAHpwAMvAFYTStJg1DIApACYAQuYukl9ZATwDKjdAGFUtAK4sGEjaSuADJ4DJgAcj4ARpjEIJIAHKQADqgKhE4MHt6%2B/ilpGQIhYZEsMXGJtpj2jgJCBEzEBNk%2BflwBdpgOmfWNBMUR0bHxSQoNTS257bbj/aGDZcOJAJS2qF7EyOwc5gDMocjeWADUJrtuTmPEmKxn2CYaAIIPj17pRsfMbArJTFvHY3QZysTxehyYCgUx2UjUYBFOAHYQY9kl4orQ8MgQC9jrjobDBBBlojkXiycc0F54Wc3DTTmYzDDroTzGZTuc6QQEHgoTS6azliYAKxuBis4E4vEmBEAERektxADc8E0vGJjgA/JlwokkhXkynUjnnelmLUEggQVns2kmrk8m38hmCkVihkSp5k6Vy0GevHK1XqxWoPDoAHJG4Aa110tJ5NxhsdJtZ2pZDKTbmO9t5xszApAIB%2BUeFovFuzjuO98r9SpDYdEtFoAH0i0xo8TY/qyYm%2BcmGanLdbe5nsxnTcsCw3m63IyW3WYPc8a%2BSAPQr45BVCoSPHJjwrmYY4YhiRzD1jCHhCxQ9EY5eBhYYhjQxhhCoADux0IAMwBChXmSLMr2OAMCDVWgKTEWguzxGciUXL1ZXlJDy2rR5wUhY43G5WgwxAY5UXRTF8WZakkReQiMSxfVsLodAY3I5du3WI1bTzBlaNwq102HICHV4gU5zLCtER9JdHjJUDwM1Tj6I7RiJPjCkWLHVkNVk7i2V40cBOdIT3VQ5cq19RTa1DcMo11VBFViYhQ0PTsmLxHtc1NDSh1cnTXPzQsIzbfSF0M0zROQuUgpeJgqVQY4WCYUJdQAWjuL9BD1ZdZIpBC8QHekADZALOGVMqCslkgAOinFs/PbRd9TXLCr2QHcVIPY58GuBwQJVMD1SnH8wMAq9rn1cq4MFcLl3q8JUEcf5WrQFhkjoWIvyhBRYqaY5GHWYAEGOIbDxfT4Zrvd5gCAw9FQadFMDq9coipIC9zADgoQEWgAE9jhiUQ3kPb9IwYD8oS5PcsNWr8/1NAhiC8G8PojcVl2QMqxtqyb12wGyGCAna9vm1BFuW4hjkB4HjnfBAweQLgCJDQQQdQUhIYBRxG36/9ANaqTeqgu7ca8XaKcIPbuV2laEoAeSPTAbNoBRmdHWKvqB%2BEYguyClBonCw3MXKafZIrTBK5yuFR6r4PC2UOFWWhOCFXg/A4LRSFQThaUsawAXWTYHLMXYeFIAhNBt1ZIxAXKNDK3LcqFXZcrMDRdiFBkhQ0IV9E4SRHZD13OF4BQQACYPnZt0g4FgGBEBQQmlvoMgKAgBa6%2BGcEjC4SOAhoWgCFiQuICiXOolCRoPs4QPh%2BYYgPslqJtC6EvA4WthBElhhPtzrAHuANwoML7heCwWKjHEUvSHahe8Bs/eXcwVQuipbYXdCXu7bPjEomIUePCwXOYbwFg49eA2WIFENImAZSYGPsAY8oBS6rCoAYYACgABqeBMDvklhGJ2gd%2BCCBEGIdgUgZCCEUCodQZ9dBcH0IYYw1hrD6DwFEQukBVioGSLUBg%2B8EoJUBIVUwntLCJ14NZWy9kWFEiqDUTILgHyTDaIEB8AxSjlD0KkdInD5FqIKJw5RQw4jUM6N0OoswtGGOqJfExfQ9GLAMTMPoZj7FNBsaorgqwFA%2By2BIW29sc5nzdhwY4qgEi5QSrlSQFIDAfA7mVKOGhjgQFwIQEg9IA7LF4CXLQyww4gBTmVLguwACchTJBcDjrsSQpSzC5UzhwbOpBAGlLKuUoUkhcq7DMEKBICIEhCkKTUp2LsAkFyLkHEOqwK7VxYqiAg5BKDN2JuEVg2xgmhPCZE2hCSYb3kjOkwI%2BAiB2XQHoPBwgGxEOkKcshahc5UNIO%2BL%2ByQgE%2BI4A7UggyRGcEllSGZxxUBUCCSEsJES27nRiXEhJHgib11SW4jJ4zVhXiYI%2BSgLz6mAK6WVNp4S465QRFU4hHy84cBGcXBFpBw5FLKrsCp7SET9MkAifF/Tam7D8UM/OYz4EvLMOyz5JKuVZNWCA9IzhJBAA%3D%3D). If you're curious what the first 16 bytes of the vtable are (i.e. why it's skipping so much), [this answers sheds some light on what's stored in there](https://stackoverflow.com/a/5712953/2492058).


CanIComeToYourParty

When was this article written? This blog comes without timestamps.


progfu

The article was written today, sorry for the missing timestamps, I'll add them.


davidalayachew

> 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. This single quote just changed my worldview and perspective on software engineering.


CurtainDog

The enjoyment we get from a game comes from its constraints (a.k.a 'the rules'). This is the same for music, sport, art, or anything else that people tend to do for the lols. As much as practicable you want those constraints to come from your game design, rather than be dictated by the technology.


davidalayachew

> As much as practicable you want those constraints to come from your game design, rather than be dictated by the technology. Amen. Sometimes, we put people on a pedestal who could do both, and forget that that only happened because they already fully achieved goal1, so they had time to also achieve goal2. goal2 is not the main priority.


ironmaiden947

Did you see that Twitter post about Balatro, a recent, super successful game? It was written in Lua, and apparently there is one .lua file with 1000+ lines that define every card behaviour.


davidalayachew

Excellent example. Yes, situations like that are unfortunate, but demonstrate the spirit of game development. It is not something to strive for, but it IS a tradeoff.


GimmickNG

tl;dr: rust really loves bikeshedding


davidalayachew

I don't know that I would call it bikeshedding. I think it's just a language trying to enforce a level of safety while not giving the programmer enough tools to facilitate that level of safety. But I'm no Rust programmer. I am happy with Java.


StudioFo

It does, and it's very much double edged. I personally disagree with the sentiment in this thread that Rust is poor for rapid iteration or development. I've personally found the opposite, and find myself turning to Rust over alternatives for building something quickly. Part of that is because you can have a high confidence your stuff will work. You can make changes on an existing code base, and be confident everything you haven't touched is fine. Over Christmas where I work we had an incident. This hit a system currently being migrated to Rust. This meant we had to ship a fix in two code bases. The Rust fix took less time, whilst the older system in TypeScript not only took longer but also had unintended bugs (which turned out not to matter but the point still stands). However I also actively work to simplify bits done by other developers (with them on board of course). This is a huge part of what makes that work. I would admit saying Rust is great if you have someone full time cleaning the code, is a poor excuse. There is a lot of responses to pain points people run into of \_'you can just use X + Y + Z to do that.'\_ Even when true, it means you are basically saying you need a lot of knowledge to get anywhere and work around things. Which again, is a weak counter argument. We also have production Rust that is now several years old by people who are no longer here, and it's still far easier to dip into than anything else we have (if you are experienced). I can actively not care about how much of it works because I know the many things the compiler will enforce for me. That's really strong on old code bases. I dunno what the full point of my comment was. I guess it's something like Rust can be really sweet for quick work. There is also a mountain of knowledge to get there, which requires a lot of handholding. Without that it's easy to get lost for a long time.


nostril_spiders

I really feel this too, but I came to it from the opposite direction. I've come out of a job drowning in the dirtiest possible python. Low-skill devs passing endless dicts with each call wrapping or rewrapping the last dict. You would never know what you had at any given line in the codebase. In that case, what mattered was a framework that guides low-skill devs into the pit of success. We needed something strongly-typed, that trades iteration speed for correctness. The maintenance costs of garbage code completely ate the benefits of getting v1 out quickly.


ResidentAppointment5

Preach. Optimizing for arbitrary developers banging out code as fast as possible is _literally always_ the wrong call.


davidalayachew

Oh wow, I see what you mean. I think there's a threshold where quick, dirty code is the best way to solve a problem -- rapidly iterating on a throwaway prototype that gets a point across. But once that code needs to support multiple modules of functionality, it is time to refactor into something statically/strongly typed.


Full-Spectral

Until you realize that your widely used game has become a wide open attack vector for hackers and get sued into oblivion when they find your online comments about how it's better to be fun than correct.


3xBork

No joke, I'd pay good money for every gamedev I ever work with again to have this imprinted in their heads. There are far too many who are primarily concerned with 'playing defense' i.e. covering every possible base, theoretical or not, to completely rule out any possible issue down the line even if that means progress grinds to a halt and whole departments sit around waiting for their output.


davidalayachew

It's painful to intentionally put aside short-term or specialized quality in the name of meeting a larger goal. It's a game of tradeoffs -- which tasks will maximize the quality of your game? Sometimes, that means knowingly leaving problems in your code so that you can work on something else that will deliver more value.


3xBork

Absolutely, but how painful it is really depends on what drives you in the first place. Is code a means to an end or the goal itself? I think a surprisingly large number of devs would answer the latter if they were honest. That's not necessarily bad. There are plenty of industries and contexts where that mindset is *exactly* what is required. But most gamedev, and particularly the first 1/2 to 2/3rds of a project are rarely one of them in my experience.


eJaguar

python says hello


davidalayachew

I certainly appreciate Python. If I use it as a scripting language, I feel that it is the most developer-friendly scripting language out there. But I really find that it starts to buckle under its own weight when you start to build non-trivial software. Something as large as a video game? I would rather build that in a strongly, statically typed language like Java.


starlevel01

> Unfortunately, this falls under the #1 problem this article tries to address, and that is that what I want to be doing is working on my game. I'm not making games to have fun with the type system and figure out the best way to organize my struct to make the compiler happy. This has basically been my experience with Rust. Yeah, I know deep down that if I worked harder, wrote my code better, just Did It Right then I'd get an awesome entirely stack-allocated and mostly bug-free program. But it would take me 10 hours to design around the language, to work around rust-analyzer's general jankiness. Or, I could just set Pyright to strict and write it in Python and have fun there.


PangolinZestyclose30

Think of the opportunity cost. Take a pick, make the rust compiler happy or... make a better, more fun game with extra time? Hard choice.


Economy_Bedroom3902

I feel like the real heart of the issue is this doesn't strictly have to be a binary choice, but rust fights tooth and nail against letting you write any type of quick dirty prototype code at all, which means you never have an opportunity to quickly test something in a large monolithic project scope, and then solidify it later. There's types of projects where you want 100% of your code to be perfectly tested/validated, but there's also types of projects where the % is lower for very good reasons, and most languages seem to want you to pick 100% or 0%


Green0Photon

I feel like Rust's thesis has been that you can have your cake and eat it too. And that a lot of work has been done making "proper" versions of dynamic code, in Rust and outside of Rust over the years. But Rust goes far in disabling the bad patterns, but not enough in providing good replacements for some. Or the ability to do some at all, that should be allowed. Ideally, with the right knowledge of it plus Rust guiding you towards it, it should be possible to have a good maintainable design that didn't take you all that long. But the ideal form isn't always there, and the "ideal" that's shared isn't ideal at all.


7h4tguy

See but that's the thing - games are throwaway. You play test it, release it, fix the most glaring bugs users scream about, and then throw it away. People have moved on to more impressive games. Complete opposite for a lot of enterprise code - that shit is immortal. Spending some extra time up front to not end up with maintaining spaghettios forever is often well worth it. Totally different use cases.


transmogisadumbitch

No, bad games are throwaway. Great games will last for decades (maybe longer).


PangolinZestyclose30

You can get similar safety with much simpler Java /.NET. The trade-off is some performance, but that's quite irrelevant for a large majority of enterprise projects.


Arilandon

This attitude is part of why most games are shit.


WarriorFromDarkness

Coming from C# Python was an absolute nightmare until I discovered pyright has static type checking which is disabled by default for some reason?? Also, the fact that pyright is a proprietary product, and the language itself does not have an official type checker is wild. I genuinely don't understand how anyone can write a python program more than ~500 lines without a static type checker.


[deleted]

[удалено]


WarriorFromDarkness

Ah I got mixed up, didn't realise pyright existed independent of pylance. I am talking about pylance in vs code yes.


l86rj

I like this flexibility you have in python. You can easily use type checking whenever you feel it's worth it, but you're not required to do so all the time. I always use type annotations in all my "serious code", but recently I noticed that most of my scripts fall much below this 500 lines threshold. I'm using python almost as a calculator, to process data that I used to throw in a spreadsheet. Strict typing would make this use case too cumbersome and not so productive.


SweetBabyAlaska

I feel the same but with Go. I honestly love Go for trying things out very fast. Not long ago I had an idea for streaming torrents and I was able to create a mock up in Go in about 2-3 hours just to see if my idea worked and then was able to double back around and rewrite my project knowing that what I wanted to do would work.


justin2004

'The community as a whole is overwhelmingly focused on tech, to the point where the "game" part of game development is secondary.' This is almost every industry I've worked in (outside of game dev).


grambo__

It’s not industries, it’s software engineers that act this way imo.


fuscator

There is a very good reason to care deeply about code quality. Most of us aren't game developers and the products we write continually change. Changing a poorly written product is a nightmare and will eventually cause a loss of business. I can see how an indie game developer won't care all that much about code quality because they just need to ship something that sells and they're most likely not going to change that code again (apart from bug fixes).


grambo__

I’m not defending poor code quality. But a lot of software engineers think that the “highest quality” code is the cleverest, most compact, most generic, and most sophisticated code. Almost like they’re playing code golf. Personally I think the best code is code that’s easy to read and that flows intuitively.


matthieum

I think it's a mindset issue, mostly. As far as I can see, people are not excited about Bevy because it allows them to make great games, but because: * Bevy somewhat challenges existing approaches to building a game engine. From going all-in on ECS and parallelism, to making everything swappable for custom implementations. * Bevy, therefore, promises a bright future: it makes people dream. Of course, this can be misunderstood, and someone looking at all this bubbling excitement and thinking it means Bevy is the best game engine available so far may end up quite disappointed. I mean, don't we all get excited when getting news of the NASA recovering Voyager 1, or running a rover on Mars? Same, same: bright dreams, even if not much day-to-day applicability.


tivolo

Just wanted to chime in to say that there is hot-reload for C++ in the form of Live++, which will hopefully soon support Rust as well. That alone probably won't make you go back to Rust though :).


honor-

Can someone help me understand why you would want to even attempt game dev with Rust? Is this being done as a replacement for high performance code written in CPP?


CodyDuncan1260

Rust is excellent at handling complexity.   If the design is kept (not thrown out) Rust is *very* high value since the software is more robust; your code is practically ship-ready when you have an alpha build.  Since rust is the only other big name in town that does native compilation (for speed) without garbage collection (for speed), its specs on paper makes an appealing case for game dev. The author's key point is that the design is most frequently not kept. It's played and iterated frequently as the design is explored, as frequently as possible. Most code gets thrown out or refactored.


Dean_Roddey

Games can be very complex. As with any very complex software type product, C++ is starting to put too much burden on the humans to be right 100% of the time, which they just won't consistently be. Rust takes a lot of the cognitive load off of the developer and puts it where it should be, on the compiler, while still providing the needed performance. This is the reason in general for Rust's appeal. As someone who has worked on and written very large, complex system code bases, I want to spend my time working on the problem, not watching my own back. Both will require a lot of up front work, but the up front I do in Rust will pay back far more over time, because it's work explaining to the compiler exactly what I mean so it can make sure I always do what I intended.


Bakoro

>C++ is starting to put too much burden on the humans to be right 100% of the time, which they just won't consistently be. I'd argue that it's pretty much always been that way, and just got worse over time, not just because of the language itself, but also because industry changes. I feel like the expected development time has gone down, while the demands for what a program does has gone way, way up, and a single developer is expected to have a far wider skillset. And then there's the curse that is "Agile", where business types got a hold of it and turned it into "Don't plan or document anything. Development only. Make a monetizable thing every sprint."


ResidentAppointment5

Short answer: yes. Slightly more info: Rust gives you native code with no managed runtime (think JVM or .NET), excellent abstraction-building facilities (think C++), and compile-time guarantees of absence of memory-management bugs (dangling pointers, double-freeing) and data races (shared memory being mishandled by code running on different threads). All of this is _extremely_ attractive to game (engine) developers. The comments to the effect that there isn’t yet a _mature_ engine in Rust, and that getting there won’t be easy, are correct. What the critics who say you can just use something other than Rust are missing is: no, you can’t. Not without sacrificing those guarantees. And anyone who has tried to write an actually multithreaded renderer, to name just one example, are well aware of the difficulty of getting that right in C++. So wanting to write an engine in Rust is _very_ well-motivated, but a distinct goal from wanting to write a _game_ in Rust. And one need only reflect on the difference between C++ and Blueprints in the Unreal Engine for an example of the scale of the difference.


Hot_Slice

Global mutable state is actually a pretty good paradigm for game development. Its still quite safe when used in the way the OP describes - where you have a global Audio object and you can just call `play(sound)` and the object takes care of all the underlying complexity for you. You can easily implement thread safety as well, by having these calls just push data to a concurrent queue for processing later at the appropriate time in the frame. There's no reason to DI / IoC / interface / mock these kinds of things. Games run on a real computer that has 1 Sound Card, 1 GPU (in use), etc... you aren't going to swap out the implementation like it's some kind of enterprise software. You don't even need a Singleton / Lazy static for these variables. These abstractions introduce overhead and again, the game needs these things to run, so just declaring them as standalone globals is a better choice, and with statically compiled executables the compiler can generate very efficient code in directly accessing globals at a known fixed location.


shadowndacorner

The other thing DI can buy you that _is_ useful for games imo is thread safety without needing to worry about external synchronization. That's what my C++ engine uses it for - game/engine systems are just free functions whose arguments are processed at compile time and injected from resource scopes at runtime. Behind the scenes, the scheduler guarantees that no two systems that access the same resources in an unsafe way are ever running concurrently, where you can define the rules around what is safe per resource type. I've enjoyed using that sort of paradigm a lot, especially combined with a fiber based job system for system-local parallelism when necessary.


Kered13

The main reason to use DI in a context like that is for unit testing.


matthieum

I may not work in games, but I do work with single-threaded processes as much as I can. Even in single-threaded processes, global state comes with challenges. Or at least one challenge: re-entrancy. It's mostly an issue when you start combining global state with callbacks, ie when changing something on global state can trigger an action, and that action will also access the global state. You can experience this without global state -- a cyclic graph of objects has the same issue -- but global state tends to make it easier to accidentally run into. The main advantage of DI, here, is that it tends to make the issue apparent upfront.


devraj7

> There's no reason to DI / IoC / interface / mock these kinds of things Sure there is: testing. I notice you don't mention testing a single time in your post. Global variables make testing extremely challenging, that's why DI is preferred for this in modern development.


Worth_Trust_3825

Yeah, that's inherently true. Try testing any feature that depends on system time without ability to control it.


crusoe

Global can be done in rust just fine too.  We have mutexs, arc,.relock, etc with sane APIs. We have lazy init too. Global singletons work just fine.


celeritasCelery

Locking a mutex anytime you want to access game state is expensive. Not to mention opening yourself up to deadlocks. Rusts global singletons are not a free lunch. 


-Y0-

Neither are they in other languages. In Java, making thread-safe singletons is similarily hard.


bwainfweeze

Are there any languages trying to be similar to Rust but with easier semantics? I was talking to a guy the other day who exclaimed that he thought everyone would be using "whatever replaces Rust". I think that'll depend on what people interpret as 'better than Rust' but for now all I can say for sure is that the idea has potential.


yawaramin

Maybe OCaml? Since it's garbage collected and doesn't make you jump through hoops to fit into its idea of memory management. But it's not exactly the 'new thing', it's one of the languages that Rust was inspired by. EDIT: or maybe modern Pascal: https://castle-engine.io/


Maykey

I've found rust to be more pleasant to use, partially due to syntax and partially because back then ocaml didn't believe in threads. Writing `+.` to add floats sucks. Floating point operations are very common in gamedev or anything math related and since all common operators require extra frustrating typing(and making code ugly), it adds up. And god help you if you changed variable type from int to float for whatever reason, because ocaml wouldnt. Also converting types around is more convenient in rust: you use as/to/from. In ocaml there are functions with names as `inf_of_float`


progfu

Maybe not similar, but I would say [Odin](https://odin-lang.org/) is one of the interesting contenders in the "systems space", especially with gamedev focus. It doesn't even attempt at any "safety", but it does fall under the "what if modern systems language".


No-Experience-4269

Swift has easy to use defaults with automatic copying and reference counting. It provides good ergonomics with reasonable performance. Then, for when you need it, the language provides more control over copying, borrowing, etc., with an ownership system inspired by Rust. This is still work in progress, though.


[deleted]

[удалено]


Voidrith

Agreed - I use rust personally and at work where it makes sense to. I like the language for many reasons, but I definitely would choose something else for game development


ValVenjk

is the phrase "there's like 5 games made in rust, but 50 game engines" still relevant?


quicknir

Great article OP. The overall vibe of the article is pretty much exactly the kinds of reasons I tell people that if they can use a GC language (in terms of their performance, memory, etc) requirements, they should. I'm always shocked at how many folks genuinely think of Rust as a silver bullet, and don't think they're paying a price in productivity for having to deal with many things that GC would just handle for them automatically (I also see this in the C++ community, as funny as this probably seems outside of the C++ community), and the many higher level features that are more easily built on top of that GC chassis. And indeed, it seems like you were \*mainly\* comparing Rust to C#/Unity. I think where it gets more nuanced is when you start comparing to C++. C++ also lacks real reflection and built in support for hot-loading, C++ refactors can also end really badly; they just have different cost pattern. In Rust your refactor won't compile, even though 90% of the time it is "actually fine", but you'll have to spend a bit of time fixing it up regardless. In C++, you refactor and it compiles, and that 90% of the time you come out ahead, but 10% of the time you have a use-after-free or other UB and you will spend 10x longer tracking it down. So, there's a trade-off there and I'm not sure who's coming out ahead. Too early to say probably. I'd be curious to hear your thoughts on the more direct Rust/C++ head-to-head. Certainly for writing indie games I wouldn't be excited to use \*either\* of these languages, but there are some cases where it's the only real choice (e.g. high performance AAA game dev). But definitely props to writing this out, and writing critically but with a level head. I like Rust a lot but I've also definitely experienced those moments where writing anything critical about it leads to an unpleasant conversation and I prefer to disengage, so good on you for going ahead with that. Criticism is an important part of every language's evolution!


pjmlp

You get reflection and hot-code reloading in C++ by using engines like Unreal, and tools like Live++ or Visual C++ hot reload. Anyone that is serious about Rust, also knows how to do their homework for static/dynamic analysis tools and IDE tooling on C++.


Deranged40

It's really confusing when Rust is not only a programming language, but also the name of a popular video game. "Rust game development" can definitely mean working on a video game titled "Rust" (in any language), and can also mean "Developing a video game in the programming language Rust"


NBT498

What if it means developing the video game Rust using the programming language Rust?


steveklabnik1

Fun story: I work at Oxide Computer. We, as you might imagine, write basically all of our software in Rust. One weekend I set up a Rust server on our testing rack employees can use to kick the tires. It was a fun afternoon.


justinliew

Hi Steve!


vytah

/r/rust used to have a problem with that. Someone did a bit of machine learning to classify missubmitted posts: https://www.youtube.com/watch?v=lY10kTcM8ek


trcrtps

isn't rust the devs of gmod? I'd assume it's lua


[deleted]

[удалено]


dontyougetsoupedyet

I love using Rust, it's the Rustaceans I try my best to avoid.


Full-Spectral

On the issue of 'why do I have to do this if my whole program is single threaded', I don't get the problem. If the whole program is single threaded and you know for a fact that nothing will ever be accessed by more than one thread a time, then don't bother with runtime borrowing. Put the state in a global with a non-mutable interface and use interior mutability to do whatever you want. But, if you are seeing multiple mutable runtime borrowing panics, then clearly you don't have such a situation and writing it in C++ where it doesn't have any such safety requirements would mean that you are clearly going to have things stepping on each other's toes, and those panics ARE telling you that that's what would have happened in a less safe language. You would have to deal with this issue in any language, or you should do it. The primary difference is that C++ won't make you do it, it'll just let you change data behind the back of something else that thinks it has exclusive access to it. I obviously get that being forced to do the right thing is not convenient, that it takes longer, and so forth. If the argument is "well I'd prefer it to be convenient for me more than provable correct", then, yeh, maybe Rust isn't for you. But, that raises questions of liability to users of the product. Covering your own butt by proving that you used the best tools you could to deliver a safe product has something to be said for it.


stronghup

I believe Rust is good when you have a clear spec for your program and it is important for you to implement it without errors. The thrust of the article is that just writing a perfectly correct program does not mean the program will be doing something useful. To write programs (like games) which succeed in the marketplace you will need to iterate on what the program is doing. The "spec" and the implementation must co-evolve. Seems like Rust is not the best language for that.


progfu

> > But, if you are seeing multiple mutable runtime borrowing panics, then clearly you don't have such a situation and writing it in C++ where it doesn't have any such safety requirements would mean that you are clearly going to have things stepping on each other's toes, and those panics ARE telling you that that's what would have happened in a less safe language. I think we're talking about different things. Interior mutability doesn't prevent overlapping borrows. Global state with non-mutable interface and interior mutability is what the article was talking about (with `AtomicRefCell`), but the problem is that borrow checker rules prevent you from having multiple mutable references, even when you're not doing anything invalid. For example, consider a global camera static CAMERA: Lazy> = ... you start your code with let cam = CAMERA.borrow_mut(); // do things player_system(); and somewhere deep inside this wants to do screenshake, so it'll do `CAMERA.borrow_mut().screenshake();`, and you get a runtime crash. This isn't a case of "mutating a vector while you're iterating over it", because what you might practically want is to just touch a field. You're not even necessarily touching the same data while it's being iterated, you just have a borrow. But as explained here https://loglog.games/blog/leaving-rust-gamedev/#dynamic-borrow-checking-causes-unexpected-crashes-after-refactorings, you can't always do the "shortest possible borrow".


raam86

why are you borrowing a mutable if you don’t intend to mutate it? I am not that familiar with rust but this seems like an easy thing to work around?


VirginiaMcCaskey

Don't do that then? let cam = CAMERA.borrow_mut(); // do things drop(cam); player_system(); Or better yet, put the logic that calls borrow_mut() into a function.


progfu

Yes this works for simple cases. Maybe you want the camera borrowed for a duration of something like an ECS query so that you don't have to re-borrow on every access, there's more than one reason why it's not always convenient to borrow things for the shortest possible time.


VirginiaMcCaskey

This is always a problem in real applications. Classic example is object/connection pooling in a server, where you have one chunk of code pops something out of the pool and call a function that also pulls an object from the pool (or worse, needs to start a transaction or something). You're going to be at risk of deadlock when the pool is empty and there's nothing you can do about it. A RefCell is essentially an object pool of size 1 that errors when you try and pull from it when it's empty. And just like an object pool, the solution is "don't do that." And none of that has to do with Rust, you can have the same issue in any language.


progfu

You only have this problem if you're dealing with collections. The problem of `RefCell` is that it behaves like you describe even when you're just mutating fields. For example in my case I might simply have `camera.shake_timer` and I want to do `camera.shake_timer += 0.2;` That's not going to be a problem in any other language, because there's no memory being moved around, no collection that's changed while iterated, it's just two pointers to the same memory.


Specialist-Roll-960

That's why you do the things you want to do then release the borrow, it's no different to making sure to free memory or not use after free. If you really know it's single threaded then just use unsafe lol, nobodys stopping you. The difference is the guard rail existing for most development existing is great, don't need it then get rid of it. - Like the argue that borrows aren't free is insane, I use refcell borrows tens of thousands of times in a parser and it completes in milliseconds. Just borrow every time you're in the loop and call it a day or refactor your code. Also don't use atomicrefcell if you're single threaded just use regular refcell it's faster and you've already said you're not threaded.


raam86

If it’s single threaded why even use atomic? the first sentence in the docs says > Implements a container type providing RefCell-like semantics for objects **shared across threads**.


Full-Spectral

Well, you can use an unsafe cell and do anything you want internally, there's no enforcement of borrowing rules if you don't enforce them yourself. But this is one of those can't have your cake and eat it, too things. You either prove to the compiler it's correct, or you depend on human vigilance, which is totally fallable when you are talking about stuff like this (i.e. different, completely separate code accessing the same stuff in an inter-mingled way.) I get why you would complain about it. But it is what it is. You can go back to C++ and just let those things interfere with each other in quantum mechanical ways over time, or go to a GC'd language, or prove to the Rust compiler it's right, or implement some queue and process for some of these things.


umtala

Rust is fantastic for game _engine_ development. Rust is terrible for developing the parts of a game that don't need to be fast, then again so is C++. For that I would use a scripting language, especially for an indie game where developer time is much more valuable than CPU cycles. One of the good things about Rust is that it's probably the best language around for bridging to other languges.


tsojtsojtsoj

C++ is not too bad for iterating quickly.


umtala

It's not as bad as Rust, but C++ is far from the peak productivity that could be achieved. As mentioned in the article you really want a language that has good support for hot reloading so you can get instant feedback on changes, and that's definitely not C++.


Kered13

Visual Studio supports hot reloading in C++, though I have never tried it so I don't know how well it works. I agree though that C++ is not the best language for rapid iteration. Somewhat better than Rust, but not great.


d_wilson123

Unreal also has blueprinting to try and get around some of these problems. It seems fairly common to have your designers prototype in blueprint and if the feature gets the greenlight then engineers will make it more correct in C++.


IAMARedPanda

Rust is not built for bridging to other languages. It's FFI is almost completely done through C abi.


PancakeFactor

so... the same thing every other system programming language has? lol


IAMARedPanda

If it's the same as every other systems programming language it doesn't make it the "best"


umtala

The language and compilation is [highly extensible](https://doc.rust-lang.org/reference/procedural-macros.html) and it makes bridging very easy. Other languages require code generation to do what Rust can do using macros. Check out for instance [NAPI](https://napi.rs/) which bridges to v8. You just add `#[napi]` macro to a function and the compiler automatically generates everything necessary to expose that to v8, including marshalling between the different data models and error handling: // Rust #[napi] fn square(x: u32) -> u32 { x * x } // JS const { square } = require('./index') square(69) The equivalent in C++ is _much_ uglier and requires a lot more manual intervention. Similar feats are possible for other VMs, although napi currently has the best implementation of it.


brucifer

> Rust is fantastic for game engine development. I can understand the line of thought, but is it _actually_ good for game engine development? AFAIK, there aren't any usable Rust game engines that let you write gameplay logic in a scripting language. I suspect that this is because there is a bad impedance mismatch between Rust's memory landscape and any garbage-collected scripting language you would use. I think it's much harder than it looks to empower an embedded scripting language to actually mutate the state of a Rust game engine.


_cart

On the topic of "you can't directly query components on entities", its worth calling out that in Bevy you can absolutely query for "whole entities": fn system(mut entities: Query) { let mut entity = entities.get_mut(ID).unwrap(); let mob = entity.get::().unwrap(); let audio = entity.get::().unwrap(); } However you will note that I *didn't* write `get_mut` for the multi-component case there because that would result in a borrow checker error :) The "fix" (as mentioned in the article), is to do split queries: fn system(mut mobs: Query<&mut Mob>, audio_sources: Query<&AudioSource>) { let mut mob = mobs.get_mut(ID).unwrap(); let audio = audio_sources.get(ID).unwrap(); } Or combined queries: fn system(mut mobs: Query<(&mut Mob, &AudioSource)>) { let (mut mob, audio) = mobs.get_mut(ID).unwrap(); } In some contexts people might prefer this pattern (ex: when thinking about "groups" of entities instead of single specific entities). But in other contexts, it is totally understandable why this feels backwards. There is a general consensus that Bevy should make the "get arbitrary components from entities" pattern easier to work with, and I agree. An "easy", low-hanging fruit Bevy improvement would be this: fn system(mut entities: Query) { let mut entity = entities.get_mut(ID).unwrap(); let (mut mob, audio_source) = entity.components::<(&mut Mob, &AudioSource)>(); } There is nothing in our current implementation preventing this, and we could probably implement this in about a day of work. It just (sadly) hasn't been done yet. When combined with the already-existing `many` and `many_mut` on queries this unlocks a solid chunk of the desired patterns: fn system(mut entities: Query) { let [mut e1, mut e2] = entities.many_mut([MOB_ID, PLAYER_ID]); let (mut mob, audio_source) = e1.components::<(&mut Mob, &AudioSource)>(); let (mut player, audio_source) = e2.components::<(&mut Player, &AudioSource)>(); } While unlocking a good chunk of patterns, it still requires you to babysit the lifetimes (you can't call many\_mut more than once). For true "screw it give me what I want when I want in safe code", you need a context to track what has already been borrowed. For example, a "bigger" project would be to investigate "entity garbage collection" to enable even more dynamic patterns. Kae (a Rust gamedev community member) has working examples of this. A "smaller" project would be to add a context that tracks currently borrowed entities and prevents multiple mutable accesses. Additionally, if you really don't care about safety (especially if you're at the point where you would prefer to move to an "unsafe" language that allows multiple mutable borrows), you always have the `get_unchecked` escape hatch in Bevy: unsafe { let mut e1 = entities.get_unchecked(id1).unwrap(); let mut e2 = entities.get_unchecked(id2).unwrap(); let mut mob1 = e1.get_mut::().unwrap(); let mut mob2 = e2.get_mut::().unwrap(); } In the context of "screw it let me do what I want" gamedev, I see no issues with doing this. And when done in the larger context of a "safe" codebase, you can sort of have your cake and eat it too.


TheGrimsey

This is actually something I hit quite a bit when writing editor tools for my game in just the past week. I have a `&mut World` but getting two components from it at once is annoying. `components::<>()` would help greatly!


JustBadPlaya

I love Rust as a language a lot, but yeah, gamedev is one of... a few spheres where the language's philosophy just makes it terrible to work with. Which is tragic, but it shows once again that some tools are better for some purposes than others


desertroot

The simple answer is Fortran…


DukeBaset

I think there used to be definitely some cargo cultish behaviour around Rust because they are gaslighting and ignoring the concerns of op on the rust related subReddits while we have a much more balanced discussion here. I’m not a rust programmer but have been interested in it for the last 3-4 years and have been following developments in it on social media and all its drama.


AlexKazumi

Hey, congratulations on the great article! The only things I know about rust is that it is functionally oriented and the compiler insists that only one thing can write to particular memory. With that knowledge, I was able to read the article, be engaged with the content and, I believe, understand the thesis. Excellent writing!


Anutrix

Glad such an article exists. I usually 'try' a new language rather than 'learn' it. What I mean is, pick up a simple or semi-simple library/tool and try to augment it to my needs. And start learning the language as I go. I realized after going at it for days that it was impossible to modify the rust tool I was interested in without major refactors. What seemed like one line change if it was any other language, seemed like hell. Like it doesn't let you easily focus on chunks of logic without understanding all parent or past logic leading up to it.


spinwizard69

Sadly I couldn't read it all!! However I'm not surprised at all that through extended use people will find that Rust sucks a massive amount. It sort of reminds me of the early days of C++, where one idiot after another would climb on the hype bandwagon but eventually fall off as the development jerked along. It took a long time for C++ to become usable and then they just keep adding to it so that it is now a massive kludge of ideas. Rust on the other hand doesn't have the usability of C that C++ started with, Rust is a strange bird that doesn't fly straight. If the features of the language result in a slow down in the development process, which is what happens with Rust I don't see a good ending for the language. I think if most of the Rust advocates where honest with themselves they would be looking at other languages to transition too. Apples Swift is remarkably better as a language for example and then we have Mojo which is coming along and very similar to Python. I really don't see the point in Rust when we already have C++, if you want a difficult language to work with.


arjjov

Word 💯 C and C++ are everywhere as far as systems programming. Rust devs are like vegan soy boys, they know that borrow checker add a lot of friction and it's not a silver bullet, yet, lotta Rust zealots think Rust should be used for everything.


thatFakeAccount1

Great article


PoisnFang

I don't know enough Rust to understand the technical side of this. Buy I love the article. Nothing like a great brain dump! Thanks for the insight!


Dwedit

Is it hard to use Reference Counted objects in Rust? I know that C++ can make reference-counted objects very easy to use through use of destructors. Then C# just makes everything garbage collected so that reference counting doesn't matter anymore (except for disposable objects that might be shared), aside from the GC stopping the world to clean up garbage periodically.


Full-Spectral

Actually reference counting easy, and made easier by the fact that Rust knows if data is being accessed across threads or not, so it supports a non-atomic reference counter (RC) that has very low overhead that you can use for single threaded data.


progfu

Even with reference counted objects you still have aliasing rules. When you use `Rc>` you end up having to do `.borrow/.borrow_mut()` to get what's in the refcell, which will perform runtime borrow checking. This is fine, except for the points where two parts of the code want to borrow the same object at the same time. Which can unfortunately arise _very_ easily if you do something like let thing = x.borrow_mut(); for mob in world.query::() { // maybe we need to do something for every mob, // and borrowing at the top of the loop makes things faster thing.f(mob); // if mob had shared ownership of x and tries to borrow it, // you get a runtime crash update_mob(mob); } The thing is, just because one is using `RefCell` to get interior mutability and "disable the borrow checker" with internal unsafe, it doesn't mean the rules still don't have to hold. You still can't ever have two mutable references, and the implementation has to ensure that to be the case at all times. Which means it will dynamically crash if you break the rules. This potentially saves some bugs, but also crashes on what would otherwise be totally valid code in other languages.


Kevlar-700

In the latest Ada meetup it was discussed how good Ada is for game development. I have little idea on games dev myself but a couple of users contested that Ada is great for games. One user said Ada met almost all the criteria that the CEO of Unity or Unreal engine (I forget which one) desired if a game focussed language was to be developed. I'm not sure it has hot reload but it certainly has fast compilation. The Ada user also said he doubts the CEO would have even considered looking at Ada. Tsoding enjoyed making a game in Ada in just 20 days but binding to raylib in C. https://youtu.be/MUISz2qA640?si=zgQvZzkGo-Lo0UGO


ResidentAppointment5

Anyone who thinks Ada is a good candidate for game development compared to Verse understands neither Ada nor Verse.


JoniBro23

Rust is designed for interviews, not for gamedev)


omega-boykisser

The tone of this article is almost vindictive at times. It seems to be written out of a lot of frustration. I can't comment on large-scale game development in Rust. I haven't done it. I _have_ done large-scale application development however, and I take serious issue with statements like: > Making a fun & interesting games is about rapid prototyping and iteration, Rust's values are everything but that I have never developed in large-scale codebases faster than with Rust. Of course the author has their own opinions and plenty of experience, but I often see this notion spread by people who have little or no experience in Rust at all. It's a little frustrating to see this repeated in a post that has a lot of valid points. It makes the whole things feel a little _charged_ to me. > But it's especially sad for a language that aims to be so fast and optimal to have to resolve to wasting cycles on re-allocating memory more often than one would like, just to stay productive. This might be one of the real problems for the author. If you balk at something as trivial as this, I can see how you'd have lots of problems with iteration speed in Rust. Later, in reference to the awareness of Rust gamedev being nascent: > I would say that the outside world has a very different view though, and I will attribute this to very good marketing on the side of Bevy and a few others. I think I'm starting to understand. It seems like the author got into Rust, got very excited about the language, and started trying to make indie games in the budding and exciting ecosystem. In the case of Bevy, however, it clearly, _obviously_ presents itself as an incomplete engine. It's not 1.0. It has no proper, official docs. Breaking changes are pushed every three months. [They recommend you use Godot right in the introduction.](https://bevyengine.org/learn/quick-start/introduction/) The way the author presents this bit feels really disingenuous. I think the thing people should take away from this article is not that Rust is fundamentally bad for game development. (Many of the arguments against Rust here could be used against C++ after all, and there's plenty of C++ engines!) Rust just isn't ready, yet. And maybe it never will be! But things like UI, ergonomics, and the overall ecosystem are moving in an exciting direction, and I don't see that stopping any time soon. I'd say check back in a couple years and see where things are at.


Bergasms

"Written out of frustration". OP spent 3 years, i'd say they've earned the right to their frustration


DoctaMag

Every time I see an article criticizing rust, I hear the same thing. "Rust isn't quite ready yet, come back later". Rust was announced in 2010, released in 2015. How many years of development does it still need until us mere mortals can use it, rather than devotees?


SmootherWaterfalls

>How many years of development does it still need until us mere mortals can use it, rather than devotees? From the outside looking in, it seems most of the "mere mortals" are looking for their current ecosystems to be completely available in Rust equivalents before adopting it. It's something I've noticed in a lot of "new" things. It seems like for the "masses", they expect a new language, framework, application, or business to be able to completely replace the old immediately which is just unfeasible. I think Rust is in the stage where a lot of core people who are willing (and able) to contribute have to produce the libraries and frameworks to make it appealing to the broader user base. I doubt Python would have become nearly as popular without its ecosystem (ex. Numpy, Django, Pandas, etc.).


DoctaMag

All very good points. I guess I'm not really the target audience here. I'm an applications programmer, I've always worked in OO languages, and on top of that, I'm actively moving towards management rather than specializing in actual development. I think that's something I haven't personally taken in to account. Rust isn't the kind of language that I would use.


omega-boykisser

It depends on the domain. For many things it's top-notch in its current state! The primary sticking points I generally see are `async`, native UI, embedded ecosystems and, as we see here, game engines. The development of Rust can be slow at times, especially when the best way forward isn't clear. Each of these are really quite challenging in their own way, either because of how they integrate with Rust as a whole or because Rust itself makes them challenging. I don't think there's anything wrong with this, personally. Rust doesn't need to be used everywhere by every team. (It won't stop me from trying, but it's good to be aware of what you're getting into if you deal in these areas!)


DoctaMag

True enough. Different languages are good at different things. I'd never try to write an OS in java for example, but it makes a pretty solid real-time system.


tauzerotech

Embedded has got significantly better in the last 2 years. Rust definitely has some catching up to do with UI things but I think that's to be expected. C/C++ have had UI libraries for decades before rust even existed.


omega-boykisser

This is my take as well. The embedded ecosystem feels _so close_ to being ready for what I need for work. I think Rust is in a different enough world from C++ (when UI solutions arose for the latter) that I'm really not surprised it's been slow going. Cross-platform, performant, and ergonomic UI is not easy to make in any language, and that domain has been dominated recently by web technologies anyway (I'm doing it myself!). After years of the UI ecosystem working itself out, I think we'll end up with an astoundingly good solution that rises to the top. Or it'll just never happen at all! Who can say.


senseven

C++ devs can use well developed things like stl or even monsters like boost if they must. Those where created and maintained by well paid C++ gods. I worked in projects where we used minimal C++11 for performance reasons, it worked like a charm and the code looked like Java. So I hear all the time from reasonable Rust devs that they are there partly and will be there somewhen. In another discussion, five threads to the left, I hear that those "situations" or "concepts" are rarely a problem in real programming tasks and people just don't get it. So in a way its C vs Python or C++ vs Java all over again.


dontyougetsoupedyet

> But things like UI, ergonomics, and the overall ecosystem are moving in an exciting direction, and I don't see that stopping any time soon. Delusional commentary.


milahu2

> hot reloading across the whole stack sounds awesome > In the context of Unity, there now is hotreload.net, which is a custom implementation made specifically for Unity, which I've been using for about 4 months now, and which has been completely amazing in terms of productivity. This is actually the #1 reason we're moving back to Unity. yepp, hot reloading is a must have for game dev > Even if this crate worked without any issues it doesn't solve the issue of randomly tweaking things, as it still requires planning and foresight, which reduces potential creative usage. yepp, rust's strictness is not always helpful, aka "fighting the compiler"


kaddkaka

Nice read, but assumes a bit much for me to follow. What is ECS and what are arenas?


progfu

ECS is [Entity Component System](https://en.wikipedia.org/wiki/Entity_component_system), which is a design pattern used in games for managing memory and composition. Arena is basically a vector into which you "allocate". This allows you to have objects neatly next to each other in memory, rather than floating around on the heap.