I would love to make it possible to bypass the orphan rule in general, including for crates published on crates.io. This is an important issue for ecosystem scaling, in many different ways.
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.
Is the orphan rule a result of some technical limitation? Or just the idea that it's "unclean" to implement someone elses traits for someone elses types?
Haskell went through this as well. Orphans used to be allowed and I certainly saw their appeal.
The problem is that the compiler might see two different implementations of ToString for MyType in different source files. The compiler could probably make a check for that if it were compiling both files at once, but if you want to be able to compile source files separately and only recompile files which have changed, etc., I think it gets harder to spot.
> someone has to hack around that with a newtype wrapper
Don't think of it as hacking around it. It's the blessed approach. Newtype wrapping is giving a proper names to the behaviours, so that they don't get mixed up.
> The problem you're having is only a problem because you haven't tried hard enough.
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.
Since every other language now has higher order functions and lexical closures and things like that Lisp isn't as special as it used to be. Now it's mostly just a really ugly version of those languages but with a weird emphasis on recursion and a very fancy macro facility.
I find I can move between dynamic languages and Scheme pretty easily.
as someone who writes elixir all day, I really love what lisp is but I feel lisp's major weakness is that it makes everythign unergonomic in order to make macros very ergonomic. You will never find another language in which writing macros is easier. the problem is it comes at the cost of doing everything else a bit of a pain in the ass.
I love elixir because it feels like the syntax clojure would have if it had a syntax imho.
> it makes everythign unergonomic in order to make macros very ergonomic
I think 'everything is unergonomic' is a too strong of a statement to be true.
I'd argue that lisp is very ergonomic for those who work with it regularly (and are into using Emacs). It sounds like you are beefing with the syntax (or lack thereof) and homoiconicity of Lisp. I can understand that. It is a very different language than others currently in the mainstream for that reason.
As far as overall ergonomics, I'd say the REPL/image-based development style and the macros that are enabled due to homoiconicity actually make it one of the most ergonomic languages in existence.
The biggest thing that prevents Lisp-likes from going mainstream is that it is too ergonomic, specifically, when you start reading a Lisp codebase, you essentially are signing up to learn a new project-specific dialect. Very ergonomic for writing code, but at the cost of understanding how that code operates.
> major weakness is that it makes everythign unergonomic in order to make macros very ergonomic.
Generally Lisp has two main goals:
* allow code to be data and data to be code
* make coding and coding code (let code manipulate code) interactive
The s-expression based syntax was found to be useful for both. For the latter it means that code can be manipulated interactively for example by structure editors or in read-eval-print-loops which work with data. That makes interactive code writing very ergonomic.
> lisp's major weakness is that it makes everythign unergonomic in order to make macros very ergonomic.
That is simply false. The ergonomics of editing Lisp is also superb.
There is a consistent, logical way to break any Lisp expression into any number of lines of text. The more you break Lisp into multiple lines, the more clearly the tree structure of the code is revealed.
The absence of ambiguity helps readability: not having to guess which expressions are children of what operator.
Lisp code sometimes makes up for the parentheses by omitting superfluous punctuation like commas and semicolons. To add two terms, we need ( ) and +. But that's all we need to add 17 terms also.
Imagine if the Unix shell required commas between arguments:
ls, -l, *.c
that's how Lisp programmers feel when back in a non-Lisp.
Julia has convinced me that the value of this is limited. It turns out to be enough to have the AST as a first-class value in the language, which can be manipulated and emitted via macros at compile time, or fed to eval at run time.
The more usual Lisps (I consider Julia, like Dylan before it, to be a Lisp) give up most of the advantages of syntax in exchange for an admittedly eloquent macro system. Julia gets the advantage of syntax, and a macro system which is of equal expressive power but less easy to use. Also, in Julia, a macro leads with `@`, the way a macro in Rust ends with `!`, meaning you can read the difference between a macro and a function at the call site. I consider that a good thing.
That’s sort of different, right? Rust is just another systems language. Lisp is the language that nobody knows but everybody secretly believes is better than their language of choice.
He mentions Common Lisp as something he did a game in, and you can see the link he praises https://www.youtube.com/watch?v=72y2EC5fkcE as basically a very well-developed Lisp image approach with timetravel debugging etc aimed at game dev. So the implicit comparison to Lisps is sort of a big undercurrent to this whole article.
A lot of the stuff he mentions he wants I kept thinking "Oh, you want common lisp!"
I dabbled in some game dev in CL and loved it. I think the ideal would be low level engine, lisp on top driving it. If you could do it without the FFI barrier slowing things down a crazy amount, it would be the killer combo.
>> The problem you're having is only a problem because you haven't tried hard enough.
And it actually does work that way with Haskell, in my experience. There's a big hill to get over where you flail against the type system, IO monad and all, and then you realize that, while Haskell's type system isn't perfect, being able to say
Num t => (t -> b) -> [t] -> [b]
is really pretty powerful, and being able to search for functions by type signature is just plain convenient.
But Haskell isn't for every problem in the way Rust is apparently made out to be. For example, I've seen some posts about developing games in Haskell, but it isn't common, and nobody would try to push a game developer into using Haskell.
> being able to search for functions by type signature is just plain convenient.
It's certainly not all the way there, but now that we are over the mostly empty promises of encapsulation and (worse) trying to model the world with classes, but grouping by that implicit first argument type for discoverability is exactly what OOP is, in this age of post-OOP.
The "you're not trying hard enough" position reminds me a lot of the Scala community's disdain for java-in-scala-syntax: if you don't use it like an almost-Haskell you're on your own (1). Here, I think Rust is actually more open: I don't read too much of the community, but most of what I saw seemed to be quite welcoming to the idea of placating the borrow checker with refcounting when "the rust way" fails to sufficiently click.
((1) but with Kotlin having taken over the entire not-almost-Haskell part of scala it's not an issue anymore: is has its niche and it fills it well, no more taking over the entire JVM while also magically converting everybody into an almost-Haskellian)
I'm comparing one unusual language (Rust) with another language (Haskell) and examining why the first gets push-back whereas the second gets more positive mentions.
They both have a single odd idea that's hard to wrap your head around at first, Rust's borrow checker and Haskell's type system with its enforced immutability by default, and which intentionally drives program design, in that both languages demand you write your program such that the static analysis can prove it's valid, but Rust's borrow checker seems to get more people throwing their hands up and leaving the language than Haskell's type system does.
Haskell's type system can be a bit inexpressive, sometimes, too, and there are language extensions which address some of this, but it seems to hit more of a sweet spot where, in most programs, if the type system is forbidding something, you really do have some kind of error in your thinking leading to inconsistent code. Plus, it enables design instead of just forbidding, because you know that any function which is valid in the type system is at least sensical to use in a given context.
Parts of the Rust community are toxic indeed, but I've been around long enough to recognize the same pattern in communities of other hot programming languages or frameworks in their up-and-coming phase.
There's something about the new hot language/framework/paradigm that always attracts the type of people who wrap their identity up in the hot new thing. They take any criticisms of their new favorite thing as criticisms against themselves, and respond defensively.
What I see now in certain Rust communities feels a lot like what I saw in Golang communities in the early days. I had similar experiences when React was the new kid on the block, too.
Some of these communities/forums are just inhabited by people who thrive on dunking on noobs all day long. The good news is that they tend to get bored and move on when the next new thing comes out.
I'm already seeing this in my local Rust community as the chronically online people are moving on to Zig now. They even use one of the Rust discords to advertise their Zig meetups and proselytize about Zig at every opportunity. Eventually they'll get bored and move on.
> > 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".
I don't fully agree with this assessment. In my experience, Rust community members who arrive with well thought out complaints or suggestions are welcomed by the people who like working on programming language fundamentals. You can nerd snipe a lot of Rust enthusiasts by showing up with a difficult problem involving tricky parts of Rust, which will often result in some creative solutions and discussions.
On the other hand, Rust communities are inundated with people trying to write Rust as if it was their old favorite language. I've spent a lot of time trying to get people to unlearn habits from other languages and try to adopt the Rust way of doing things. Some people refuse to let go of paradigms once they've used them for years, so doing things the Rust way isn't going to work for them. I've worked with enough people who spent more time fighting against Rust than trying to adopt Rust ways of doing things that I've also reached the point where I don't engage once I see the conversation going that way. Can't please everyone.
> Parts of the Rust community are toxic indeed, but I've been around long enough to recognize the same pattern in communities of other hot programming languages or frameworks in their up-and-coming phase.
I would say that in general, this type of attitude permeates a lot of software engineering, and even engineering and science as a whole. When I speak with people in other fields, particularly more creative ones, the discussions are so much more improvisational and flowing. Discussions with software developers, engineers, and scientists have this very jerky, start and stop "flow" to them as you meet resistance at each step. I honest to god have had people telling me no or shaking their head no before I ever finished my question or statement, much less before they even understood it.
> There's something about the new hot language/framework/paradigm that always attracts the type of people who wrap their identity up in the hot new thing. They take any criticisms of their new favorite thing as criticisms against themselves, and respond defensively.
You're spot on about the coupling of someone's identity with it. Rust especially seems to also have this never-ending gold rush to be the next framework and library everyone uses, which creates a very competitive landscape. And it seems most frameworks and libraries get 80% of the way and then fizzle out once people realize Rust isn't a magical language that solves all your problems.
Conversations that people have in science and engineering are more analytical and pessimistic, where they try to clarify and set the boundaries.
This is the opposite of creative and optimistic conversation, where you break boundaries between things to create something new.
The attitude to be analytical in conversations comes from the fact, that software engineers usually do creative part of their work alone, and use communication for analytical part (code review, requirement clarification, etc). For some creative people it's natural to do creative work together, especially in music, so it's easier for them to adopt a creative attitude in conversations.
There are many different ways to solve problems. Having tunnel vision will exclude most of them. Critical thought has its place in any field, but many scientists and engineers will hide behind so called analytical thought when in reality, the ideas are more biased than they'd like to admit.
I think problem solving has analytical and creative parts too. Like in Polya's 'How to Solve It' you have clear analytical steps in the beginning (what's known, what's unknown, etc), then 'Boom! Heuristic!', then again analytical steps for reflecting back on the solution (corner cases, did you use all the inputs, etc).
> What I see now in certain Rust communities feels a lot like what I saw in Golang communities in the early days. I had similar experiences when React was the new kid on the block, too.
Go was released in 2012, Rust in 2015. Are you saying we are still in the early days of Rust?
You’re moving the goalposts though, the question was about Go. Which 3 years ago was nothing like Rust today. Because it’s much more pragmatic “here’s the trade-offs we had to make and why” language. Growing in and out of a cult isn’t a natural part of a language’s evolution like the GP comment suggested.
This is definitely untrue. I was bullied out of the golang community for asking about generics when I first started learning it. Obviously, I don't think my experience is indicative of the entire community, but the experience from the community left a bad taste in my mouth.
> Go was released in 2012, Rust in 2015. Are you saying we are still in the early days of Rust?
Release dates mean very little. Golang had rapid adoption early on. Rust only recently became one of the fastest growing languages. Golang stabilized a lot of things about the language and library early on. Rust still has a lot of common features gated behind nightly.
Ruby was released in 1995 and didn't really have its heyday of developer hype until Rails came along a decade later. Python didn't start gaining serious traction for about a decade after its 0.9.0 release either. Go's immediate uptake seems like an outlier in programming language lifecycles.
"On the other hand, Rust communities are inundated with people trying to write Rust as if it was their old favorite language."
This is perfectly understandable if you view languages as tools. If the new tool can't do something the old one did, then you'll have questions about using it. A great example is inheritance - if inheritance is missing, that's a negative for me. I don't care about the philosophy, I just want to use the tool to produce programs better and faster. If it's missing features, that's a negative point.
> Parts of the Rust community are toxic indeed, but I've been around long enough to recognize the same pattern in communities of other hot programming languages or frameworks in their up-and-coming phase.
Yeah. I think there’s also a weird way that newcomers get treated when they join a community. When you’re a newcomer to Rust, you probably have some preconceptions about how Rust should work based on the other languages you know, and you’re probably running into a lot of the same problems with Rust that everyone else has (e.g. borrow checker).
Most of the community is just kinda tired of the discussion and tired of answering the same questions, so they don’t interact with the noobs at all. That means that the people who, as you say, “thrive on dunking on noobs all day long”, are the primary point of contact for noobs.
Finding a decent programming community these days is a pain in the ass. The cool people, i.e., the people working on cool projects and getting shit done, are mostly busy and not hanging out with anybody.
This was my biggest problem with rust, the community is, at a surface level friendly, but the moment you try to say something against the grain you get met with some of the most insufferable people ever. I tried to mention a problem with a lint on the official forms and the replies were so condescending (from regulars at that too). And at no point did they try to understand the issue, they just assumed I was dumb/new/doing something wrong.
Rust is a language with a very high mental cost to use effectively. It requires some fundamental paradigm shifts in the way you write software, hence it's a hard language to be productive in.
For some tasks, the tradeoff is worth it. Some individuals are naturally very inclined to use it.
But for most people, for most tasks, it's simply not the right tool. It's not a general purpose language by any stretch of the imagination.
If there were only two programming languages in the whole world, just two, no more; one better fitted for OS kernels and one better fitted for game programming, which one would be considered a general purpose language?
I would argue, OS programming is definitely more general than game programming. Rust is great for OS related tasks, math, deep learning n other stuff, and maybe not so great at GUIs, or even bad at that, or just inconvenient as the article points out.
That means, it should be used for general stuff, and for programming games should be used a niche language, like Lua, Python etc. Especially for fast iteration and experimentation, break things and move lightning fast, typed languages are in disadvantage for sure, compared to untyped/weakly typed.
I would argue that OS kernel programming is actually a very specialized niche of software engineering; it demands near iron performance, fine control down bit level granularity, perfectly deterministic memory manipulation, flawless security and maintainability, and all at the same time. These are conflicting requirements and meeting them naturally spills over into decreased productivity, which is acceptable because OSes are well engineered and evolve over decades.
If C or Rust would not be available, you could still write your kernel in a well documented assembly dialect - some people still do - and then complete your OS userland in some garbage collected/interpreted/WASM abomination. But it's unlikely you could write Microsoft Word or a modern browser in assembly.
And this I think is true for games too, since, other than the performance requirement, they don't need the other features of Rust and are quite tolerant of failure. They also tend to be unwieldy beasts hard to design cleanly because the internal world simulation would become ever more complex, imposing weird data access patterns etc. If your online play code suddenly requires access to some internal "tank fuel" state, in C you would just pass the pointer and pray to the goods of rock and roll it's still valid at access time; a garbage collected language would at least give you that; but in Rust, you are looking at a complete refactoring of the entire "tank" module to make the new access pattern fit the ownership model.
As the post explains, this will kill quick iteration, which I think is a very general need of most programmers.
I don't think it holds them back. Rust is just not an amazing special tool for game dev it seems, doesn't matter how the community behaves. It's still a solid choice for systems.
Has there been any progress towards shipping Rust on consoles? I know the specifics are all under NDA, but to my knowledge nobody has even hinted that they've done it yet, even among the studios which are openly using Rust for backend or tooling stuff (e.g. Embark and Treyarch).
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.
Rust has had tier 3 support for the Switch since 1.64, I believe. Someone had actually done it even before that but IIRC NDAs mucked up any movement on making that public.
Yeah, that Switch support was added by the homebrew side of the fence though so I don't know if it's something that developers would be able to use in an officially licensed game. Nintendo might not care so much as long as the game works, as mentioned it's Sony in particular that I've heard is picky about which compilers their developers use.
If they tried to use C++ it would have ended absolutely the same way.
As someone who works in gamedev I can assure you C++ is same bad choice for 2 man indie project. In search of fast iteration times games have moved away from writing code in low level languages. Hardware for casual games is much bigger and faster than anything small team is able to make.
Yes, and if you want move fast and don't break things you need a higher level language like Go, Java, C#, etc.
I'm pretty fast in Rust but not as fast as Go, mostly because Rust's type system and borrow semantics come with a higher cognitive load.
I find both to be faster than C++. Rust is faster because I have to worry less about blowing my feet off with memory errors. I can't think of anything to recommend C++ now that Rust exists.
Yeah, I rather like the pattern of embedding a more rapid iteration scripting language on top for game stuff like Lua or stripped down Python variants, using Rust as the core and hot loop code. That said, there is a lot of folks wanting to do it all in Rust, and the article touches pretty well on that not being the pleasant case for a lot of valid gamedev approaches.
Can you please give an example of that? Rust is very much advertised as a systems programming language. Can't really blame people for using it and going "oh it's harder than go and python"...
I would think a modern systems language such as Rust wouldn't require a systems development methodology from the 1970's. There's no inherent reason you shouldn't be able to rapidly prototype a system and then refactor the prototype into your actual implementation.
Rust is the "frontend wave" of the backends. All the woke kids gravitate towards it. A suffix of "Written in Rust" is a thing here so I'm throwing away my internet points now.
I do not complain. I've seen this trend multiple times by now. First time was with Ruby on Rails I think [1], then the cancer that led us to write JS to output HTML and all the good stuff that came and still manages to pour from npm. Every now and then you can spot the "fashion" out of these things.
Rust is good and has earned its place. I just despise cult-like followings for these languages and technologies.
There's no need for you to blather about "woke" and drag in US culture wars nonsense. It's distasteful, glib, and frankly not very intelligent, and just subtracts from whatever your point was.
I work fulltime in Rust (embedded stuff) but actually think the "everything else" influence into Rust is stronger than the systems stuff, and it's harming the ecosystem.
Just try finding e.g. an MQTT or WebSocket or etc library that doesn't drag the whole mammoth tokio ecosystem (which is really geared for Web Scale! projects) in with it.
Rust is becoming the language that tokio ate, and Cargo/Crates.io the new NPM.
That is, Rust is the systems language that a wave of non-systems developers insist on using, leaving behind a trail of non-systems-appropriate crates and projects.
Parent commenter's comment was crap by HN's commenting standards... but the underlying point about trendiness I think is in fact accurate.
I used Rust professionally up until this year (since before async landed) and IMO you've hit the nail on the head. The shoehorning of async into the language and ecosystem seemed to be driven by the belief that if Rust can't attract Go or Python developers then it will fail. In fact Rust was doing fine in its niche attracting C++ developers and trying to make it everything to everyone has diluted what was great about it.
(That said, your example might be a bit exaggerated as I found mqtt libraries that don't require tokio).
I might be inclined to buy this complaint if tokio was part of the standard library. But it isn't, and I just can't see how its mere existence and tree of dependents has a negative effect on the "ecosystem", it's not preventing anybody interested in "embedded stuff" from producing high quality libraries that are not dependent on tokio, and I don't buy that there is some fungible mindshare being taken away. This to me seems more a gripe that Rust is not being adopted quickly enough in certain domains, which I believe has far more to do with both reasonable and unreasonable inertia.
(How is tokio fundamentally different than boost.asio and beast in this regard?)
> That is, Rust is the systems language that a wave of non-systems developers insist on using, leaving behind a trail of non-systems-appropriate crates and projects.
I have hard time understanding how this "trail" of stuff (evoking imagery of pollution) that no one is forced to depend on nor is promoted in any special way in the core library or language is any meaningful impediment to developing and distributing libraries more appropriate to the embedded domain, but perhaps one can explain how.
It can be though. You should be able to express low-level details but also implement high-level constructs effortlessly should you so choose.
Look at C++, which can both dick with move semantics but also offer multiple inheritance. Even better, look at C#, which has pointer fiddling but also offers the best reflection of any language today.
You're right that Rust is only really good for kernel development but it didn't have to be that way.
tl;dr -- Rust is not the language for startups who want fast iterations and are still finding it's pmf. It is perfect for rewrites of popular system software which already has millions of users and running in production because once a software becomes mature and at million user scale, safety and security becomes paramount
> for rewrites of popular system software which already has millions of users and running in production
Hopefully, it's perfect for more than that.
Rewriting a system that's already acheived that scale is a contentious decision, to say the least. Many of our colleagues have been lost to and scarred by well-meaning attempts to rewrite those systems in the past, including many who were sure it was the right choice for "safety and security" themselves. For every conceptual safeguard new tooling might give you, you invite countless and sometimes catastrophic regressions in actual application logic in writing new implementations. It can work out, but no tool can expect to live only on that kind of work.
I'm sure Rust has much broader applicability than that. Or I hope so at least.
At million user scale, feature parity and compatibility is truly paramount and, `rg` aside, Rust utils don't have a great story to tell there. Such software has evolved in the course of decades and its (usually despair-inducing) code is tailored for dealing with a tremendous number of edge cases, platforms and architectures. Rewrites in Rust tend to write "the cool parts" and indeed write them better and faster, but that is just not good enough for replacing the standard tools.
> I'd argue as far as maintainability being the wrong value for indie games, as what we should strive for is iteration speed.
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.
I copied this exact same snippet and was going to comment the exact same thing.
Why choose Rust if you don’t care about maintainability and long term stability? Those are core values of the language!
The language choice was wrong from the start. C++ is king for games so if you care more about delivering features and fast prototyping why not go with that?
Maybe Rust is not a good language for rapid iteration in the game industry. And that’s ok I think.
Right. Yes, you are correct. I assumed C++ was the language they replaced with Rust because they wanted to write lower level stuff, like their own engines (and they mention doing that).
It makes even less sense to use Rust to replace one of the higher level languages like C#.
Rust is not antithetical to iteration-based programming, it just makes you write a lot of heavy boilerplate to explicitly support that kind of style. The flip side of that is once the 'iteration'/'prototyping' phase is over, you can actually refactor the prototype into high-quality production code, instead of either throwing it away altogether and rewriting it from scratch (spoiler alert: this doesn't really happen most of the time, because it's viewed as pointless waste) or just putting it in production as-is (which is what people actually do, even though it's obviously a disaster in the longer run).
Surely there's better choices if the primary desire is iteration speed, and a secondary or maybe even tertiary desire is maintainability/refactorability
Rust gets in the way a lot, as it's supposed to for safety. Maybe it'd be a lot faster to iterate on if some AI could auto fix your code to make the compiler happy.
No, Rust is pretty antithetical to iteration-based programming. The language basically requires you to plan for the ownership model from the beginning, and it can be quite difficult to retrofit a changed ownership model into an existing program.
I've run into this in a side project I'm working on, where my indecision over which ownership models are actually workable in the API to satisfy the needs I have means almost all of the coding is spent just proving I can get an ownership model working over having a skeleton code that can do something. And still, even as lacking as functionality as this codebase is, swapping to a new ownership model takes several minutes of coding. Trying to do this kind of exploration on a codebase that has real functionality would mean spending hours of change just to move some property from here to there.
If you're genuinely unsure about the ownership model (this is pretty rare in practice though) you can just use Rc<>/Arc<> (which allow for shared ownership via refcounting) and be no worse off than if you were coding in Swift.
I've ended up with similar thoughts about automated testing in games too.
I really enjoy having automated tests and automated tests solve problems. Like, I absolutely love our molecule test suite for our infrastructure code and it gives us huge value. The smoke tests we have for the infrastructure running after deployments are amazing. It's liberating in an environment of rapid and chaotic changes everywhere, complex interdependencies, as well as pressure on top.
However, if I try to transfer that kinda approach to a game it just... fails?
But the realization is: In games, many behaviors and systems are actually far simpler and less intertwined in arcane ways, and code changes actually less frequently and dramatically than at work.
I could see structured testing in e.g. turn based strategy games and such, so you can test that the culture per turn is correctly calculated based off of many different situations and such.
But in many smaller projects I've had, you write some janky code, make sure the enemy behaves as expected... and never touch or change that piece of code ever again. And it just doesn't break, because no fundamental part below it ever changes, because then the entire house of card would fall apart.
It feels dang weird, but it works very well for smaller projects.
The Rust community is one of the top arguments against rust.
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.
My understanding is that he rather asked for specific examples (maybe post on reddit, rustlang forum), maybe I am wrong. I think giving actual examples would be more beneficial so we see the actual context clearly.
I don't do this usually because it's not constructive and incites flamewars, but since it was asked, this is whats 5 minutes of browsing comments got me.
> 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.
As a newcomer, not thinking the Rust way is a sin and if you ask dumb questions you deserve retaliation, apparenlty:
> "On the other hand, Rust communities are inundated with people trying to write Rust as if it was their old favorite language..."
> "..In my experience, Rust community members who arrive with well thought out complaints or suggestions are welcomed by the people who like working on programming language fundamentals."
More handwaving of Rusts shortcomings. Plus contradiction on the same sentence:
> "Rust is not antithetical to iteration-based programming, it just makes you write a lot of heavy boilerplate to explicitly support that kind of style."
Many of us have probably heard that all the dumb programmers use Javascript, but even if that's true, that's a reason for me to use it. Even though I'm capable of using galaxy-brained language ecosystems, I don't need that mental overhead. And if it'll be cheaper to hire JS programmers onto the team, great.
Unity is so good and quite affordable, basically there’s zero upfront risk of using it. Similar for Unreal Engine. And then there are tons of other open-source engines like Godot that are also quite good.
Rust is great from lots of stuff but game development or building UIs isn’t among that (yet).
To be fair, building UIs with iced-rs is getting better by the minute. My favorite showcase for using that library is this IRC client called halloy: https://github.com/squidowl/halloy
Unity learned that they have to turn the temperature up slower. They kept the new license that everyone was mad about but just made it so you can keep your current license if it's cheaper for you. No doubt they'll be tweaking these values over time.
Yeah, that's definitely a concern I have. Well, projected concern I guess, I'm using Godot myself rather than Unity, mostly because I found Unity way more confusing when I tried to learn it. But not needing to worry about licensing with Godot is certainly a nice bonus.
It was changed to being able to keep the old license if you don't upgrade to Unity 6 or beyond. They added 2.5% revenue share as an option to the flat runtime fee to make sure you can't end up in a situation where you are losing money per user just from the runtime fee alone. Unity by default charges for whichever ends up cheaper.
As much as I love Rust I sometimes wonder if I'd be more productive in a simpler language. If I wrote it every day I'm not sure that would be true, but as a hobbyist coming back to Rust sometimes takes me a bit to get back in the zone. Also, still not a fan of async, as it is woefully incomplete and fairly complicated in some use cases. That said, I just can't go back to Go with nil pointers and lack of decent enums/ADTs/pattern matching either. I long for the "in between" language, but with an amazing 3rd party ecosystem as both Rust/Go have.
The problem with languages is they don't compose. Iow, one missing feature needed automatically invalidates the language entirely. Swift is targeted at Apple platforms and cross platform is an after thought. Kotlin targets JVM and while it is cool in concept, I hate it as a user (and Kotlin native isn't near as mature). If were considering something else at this stage I'd probably put my time into F#, but even it has its cons.
NOTE: By "compose" I mean that if I want feature A, B and C from lang X (has A and B), Y (has B and C) and Z (has A and C), but there is no way to get A, B and C in one language without creating a brand new one. I cannot mix and match features from different languages.
They don't compose what? Is this one of those 'monad is an endofunctor' deals?
(me not knowing might be the ignorant bliss that allows me to just do productive things in them regardless)
Edit: I understand from the updated comment
I've long accepted that no one language can offer all of the language features I'd want, but I also question if I'd even want that language if I got it
I've been hoping to see more languages that compile to Go, it might be the most practical way to arrive at what you want. For example, see Borgo: https://news.ycombinator.com/item?id=36847594
If you do, I'd ask you to give some thought to FFI efficiency. I've been wondering if there could be a good way in a transpiled language for the type system to validate at compile time that any pointers passed to C are pinned, so you can safely run the FFI calls with cgocheck=0 which eliminates quite a bit of overhead.
Maybe people will make fun of me, but I've been very happy with Kotlin and Dart.
Null-safe, good ergonomics, very fast.
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 glad you found tools/languages that work for you. Kotlin felt a little too much like Java to me. If I stuck with a JVM lang. I'd probably go back to Scala 3, but I just don't like the JVM as a user (just sucks too many resources).
I like Kotlin but I find the fact that it lacks a good way to detect and handle possible errors very frustrating. If some function can fail on sane-looking input I'd like to know about it
(Judging is still ongoing and votes would really be appreciated! It would help me to get more resources to work on the underlying package).
This was my first ever “game” (tech demo, really), and I’m not a game dev, so take this with a grain of salt - but I do think there’s a lot of potential for Flutter/Dart as a game framework. Hot reload makes iterating on game logic very fast, you obviously get the UI toolkit and cross-platform support straight out of the box, and the language itself is (relatively) concise, so it lends itself well to gameplay programming. When you need to get your hands dirty at a lower level, you just drop down to C++ (or whatever engine you can expose via FFI).
I think Google believe that Flutter can nab market share from Unity in casual 2D games (hence their official sponsored competition), but I think it has even more potential than that. In fact, I’ve seen at least two game companies (Supercell and another whose name I’ve forgotten) hiring for people to work on embedding the Flutter engine in various platform games.
Rust is in a separate class of go and swift and kotlin and etc. The class it competes on is pretty C, C++, and itself.
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.
For Apple, Swift is their Rust, regardless of the world outside Apple's ecosystem thinks about it.
It is clearly stated on Swift's documentation, they already hold a couple of talks at C++ conferences about code migration, and is one of the reasons why nowadays they mostly focus on LLVM contributions instead of clang.
> I just can't go back to Go with nil pointers and lack of decent enums/ADTs/pattern matching either.
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.
No, Ada/Spark is an example of a good engineers language. Go is a mediocre effort at best. Rob Pikes defence is that it was designed for junior Googlers who "aren't capable of understanding a brilliant language". Yes that's a real quote.
I ran into these issues all the time with Java, C++, and Python projects.
But it's just not the experience of running Go in production, which I've been doing for over 10 years now, across many projects with many devs.
In practice, nil checks are just not very difficult to include everywhere. And experienced Go programmers don't use exceptions (panic/recover) almost ever.
2) Based on faith that someone will not forget to do something instead of a well documented mechanism in the language that could block that from the start
Having nil/null to handle empty references is simply very bad design and there's decades of examples why. The correct way is using a two-value type like Option, Maybe, etc. so that the (possibility) of the value missing is actually encoded in the type system
Having worked with Go for about a decade now, I largely agree that nil is a pain in the ass to work with, and the language has largely done nothing to make it better. However, Go (mostly) doesn't have exceptions. Ordinary problems are represented by non-nil errors, which are values. Panics exist but really are reserved for exceptional situations (with the number 1 cause being, of course, dereferencing nil).
There are a lot of ways in which Go handles nil better than C handles NULL. At the very least, a panic is better than a segfault. And carefully written code can avoid most kinds of nil panics entirely. So I guess the language's authors thought this would be enough to overcome the mistake. But I don't think they went far enough. The very limited type system and the lack of nil-safe operators make it not very ergonomic to write and read such "carefully written" code; design decisions in some key parts of the standard library completely undermine the language's attempts to minimize nil; and then there's the "untyped nil" default value for interfaces, which panics if you just look at it funny.
And it will still take time to come, the whole engineering problem is how to introduce value types, make classes that are clearly value types like Optional, turn into value types, while at the same time not breaking the endless amount of JAR files in production, when upgrading to a JVM with value types support enabled.
They would get it sooner by breaking the ecosystem, and as Python 3, Java 9, .NET Core have shown, not everyone would be racing to adopt the new shiny thingy.
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.
I can echo OCaml. It is probably the most underrated language imo. It has great compilation times, macros, type inference, good tooling. With few exceptions, the library ecosystem doesn’t suffer from the same overengineering issues as Haskell and types are kept relatively simple. It has simple runtime characteristics making it easy to optimize performance when needed, although it tends to be very fast in general.
last I used OCaml, the standard library situation was abysmal (compared to say Haskell's), and you had to go searching for third-party "batteries included" crates to cover simple stuff. Has that gotten any better the last few years?
It doesn't occupy the same space, but the simplicity of Gleam has been very enjoyable to me. It's still quite a young language though, but worth keeping an eye on.
As much as I dislike many of the legacy issues with JavaScript I find TypeScript to be the best language to iterate with. If Rust had GC without need for wrappers like RC though I think that would be my preferred iteration language.
I mostly try to write my TS in a manner that would translate to Rust, but that's hard to do sometimes when it comes to memory management.
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.
If there's interest, I can split some of my code into stand-alone chunks and post my experience of what worked well and what didn't.
I wanted to share some thoughts on here on what brought me to F#. Maybe this can serve as a starting point for people who have similar preferences and don't know much about F# yet.
A big part that affects my choice of programming language is its type system and how error handling and optionality (nulls) are implemented.
That "if it compiles, it runs" feeling, IMO, isn't unique to Rust, but is a result of a strong type system and how you think about programming. I have a similar feeling with F# and, in general, I am more satisfied with my work when things are more reliable.
Avoiding errors via compile-time checks is great, but I also appreciate being able to exclude certain areas when diagnosing some issue.
"Thinking about programming and not the experience" the author lamented in the blog post appears to be the added cost of fitting your thoughts and code into a more intricate formal system.
Whether that extra effort is worth it depends on the situation. I'm not a game developer, but I can relate to the artist/sound engineer (concept/idea vs technical implementation) dichotomy.
F#'s type system isn't as strict and there are many escape hatches.
F# has nice type inference (HM) and you can write code without any type annotations if you like. The compiler automatically generalizes the code.
I let the IDE generate type annotations on function signatures automatically and only write out type annotations for generics, flex types, and constraints.
I prefer having the compiler check that error paths are covered, instead of dealing with run-time exceptions.
I find try/catches often get added where failure in some downstream code had occurred during development. It's the unexpected exceptions in mostly working code that are discovered late in production.
This is why I liked Golang's design decisions around error handling - no exceptions for the error path; treat the error path as an equal branch with (error, success) tuples as return values.
Golang's PL-level implementation has usage issues that I could not get comfortable with, though:
file, err := os.Open("filename.ext")
if err != nil { return or panic }
...
Most of the time, I want the code to terminate on the first error, so this introduces a lot of unnecessary verbosity.
The code gets sprinkled with early returns (like in C#):
public void SomeMethod() {
if (!ok) return;
...
if (String.IsNullOrEmpty(...)) return;
...
if (...) return;
...
return;
}
I noticed that, in general, early returns and go-tos introduce logical jumps - "exceptions to the rule" when thinking about functions.
Easy-to-grasp code often flows from input to output, like f(x) = 2*x.
In the example above, "file" is declared even if you're on the error path. You could write code that accesses file.SomeProperty if there is an error and hit a null ref panic if you forgot an error check + early return.
This can be mitigated using static analysis, though. Haven't kept up with Go; not sure if some SA was baked into the compiler to deal with this.
I do like the approach of encoding errors and nullability using mutually exclusive Result/Either/Option types.
This isn't unique to F#, but F# offers good support and is designed around non-nullability using Option types + pattern matching.
A possible solution to the above is well explained in what the author calls "railway oriented programming": belinder↗
Thanks so much for your detailed reply. This looks very cool indeed. I've had a couple tiny projects in F# in the past that never went anywhere, but you're describing essentially all the parts in a programming language that I want, early returns, binds/maps, language support for these features, defining your own keywords (not really but kinda with your expressions)
Like the author states, the "written in Rust" bonus many projects get does not apply to games. Most of the games I consider to be the most fun are built on an absolute rats nest dumpster fire of code (see: https://www.youtube.com/watch?v=k238XpMMn38). Sometimes bugs even expand game mechanics and make them more fun and expressive. That being said I'll definitely check out Unrelaxing Quacks, it looks great.
My impression of Rust is that it's a very opinionated language that wants everybody to program in a specific way that emphasizes memory safety above everything. That's a good idea, I think, for the systems programming use cases that it was intended for. I don't see that as a particularly useful thing to value for game development. The part in the article about the Rust borrow checker constantly forcing refactors sounds extremely obnoxious to deal with.
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.
IMO it’s not opinionated enough. Golang for example doesn’t let you customize go fmt, while Rust does. Rust also has many ways to do things in general (mod.rs vs name_of_folder.rs for example) and seems to not want to provide a useful baseline for most projects via its standard library (unlike Golang).
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.
mod.rs came first and that made creating modules verbose as hell. Not only that but imagine having many mod.rs files open, you wouldn’t know what module you are in by just looking at the filename.
Open 5 libs.rs, which crate are you in? Always look at full path instead of filename. A mod.rs means a much more cleaner organization, as you know that all of a folder module's content is within that module, and you don't have to look elsewhere.
> My impression of Rust is that it's a very opinionated language
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.
Rust also is opinionated that you don't want to write shared libraries or plugins. You can do both, but only if you drop down to memory unsafe C interfaces. The default is statically compile all applications into one program. Rust also really wants you do to use their build system and package manager, you can avoid both but everything will fight you.
> Rust also is opinionated that you don't want to write shared libraries or plugins.
Not having a solution is not the same as having an opinion.
If you have years to spend on plugging at ABI stabilisation, generics and proc macros in dynamic linking, and redistributable std, I’m sure the core devs would be happy for you to.
> Rust also really wants you do to use their build system and package manager, you can avoid both but everything will fight you.
What do you mean everything will fight you? It sounds like you’re confusing rust and its ecosystem.
> 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.
This isn't even close to being true. I think memory safety isn't as important for games as it is for most software (though it is still quite important for multiplayer games!). But even if you write the most modern C++ possible I guarantee you are going to spend some of your time debugging segfaults, memory corruption and heisenbugs. Don't try and claim "I don't write bugs". Everyone does.
That assertion was specifically qualified in the context of database engines, for which it is true. I definitely write bugs but I haven't seen a segfault or memory corruption in years. That is more of a C thing than a C++ thing.
It is kind of difficult to have a segfault or memory corruption with explicitly paged object memory, since there can't be any pointers and these complex objects are bound-checked at compile-time. If you care about performance and scalability, you don't need to concern yourself with multi-threading as an issue either. The main way you'd expect to see memory corruption is if you try to read/write a page in the middle of a DMA operation to the same memory, and Rust doesn't help you with that either (though this would be just a normal logic bug in the scheduler).
It is pretty easy to avoid segfaults and memory corruption in modern C++ if the software architecture doesn't allow you to create the conditions under which those are likely to occur.
That doesn't seem to be at all what they're saying, but in any case I checked SurrealDB (biggest Rust DB I could find) and there was exactly one report of a segfault and the developers couldn't reproduce it.
As far as I can tell about 5% of mariadb bugs mention segfaults, compared to 0.2% for SurrealDB.
I mean it's fairly obvious that even if some code in a Rust database is `unsafe` because it deals with manual paging and DMA and whatever, most of the code is going to be safe code.
That is significantly dependent on the software architecture. MariaDB's design is not particularly modern (not a knock against MariaDB, it is an older system) and employs none of the software architecture required for high-scale and high-performance kernels that, as a side-effect, makes it difficult to accidentally create the conditions for a segfault regardless of the language. The design motivation is actually optimal dynamic resource scheduling under heavy unpredictable workloads, not memory safety. Rust's borrow checker doesn't work with these memory models, so you'll be in the same boat as C++ regardless.
I always found it theoretically interesting that schedule-based safety architectures, which are focused more on optimal resource allocation than safety per se (its all about extreme throughput traditionally), asymptotically converge on memory safety too as a practical matter for the same reason they also require almost no locking. By doing the safety analysis (many kinds, not just memory) at runtime, tiny dynamic modifications to the execution schedule are sufficient to provably (using TLA+ and similar) avoid many types of "unsafety" without the design compromises required to enable some of this analysis at compile-time. It requires a non-traditional software architecture, and it doesn't play nicely with a lot of existing code, due to the level of execution control required but I see more and more systems being designed this way at the high-end of the data infrastructure market.
This kind of extends to embedded/low level systems programming as well - the assumption that memory can only change as an effect of program execution just does not hold true there. What's the value of tracking mutability and data ownership when a DMA engine can just decide to overwrite the memory you supposedly have exclusive access to?
Sort of, although Zig certainly pushes itself towards the embedded world. I have tried Zig a bit and like it a lot, and I am sure it would be better for game dev than Rust, but I don't want to pass allocators around all day to all the objects in my game.
nothing really prevents you from defining global allocator in Zig
and having explicit allocator in standard library is actually a good thing, cause it's quite a common case in game development to use arena allocators which are being freed once per frame - so you don't really need to reinvent your data structures in Zig
I do have some concerns about Zig because it also introduces some friction for correctness sake like requiring to always use all variables, explicit casts everywhere - I want some compiler toggle to disable all of that and focus on problem but unfortunately it's not there
I am playing with Zig now and haven't really formed my opinion about game development specifically but I like it a lot so far
To be fair though that sort of friction only affects things in the small. They can be annoying but you'll never have to refactor outside the scope of the friction itself.
In practice, it's only really a problem if you're doing codegen.
Zig is flexible. If you don't like global allocators or explicitly passing allocators, you can store pointer to the allocator in your object and it will be passed implicitly.
> 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.
For me, this is Odin-Lang, it doesn't meet all the requirements you have listed, but it's ergonomic, fast, and comes with extensive core and vendor libraries. It's all just fun and reasonable.
Most modern languages are memory safe and they don't get called out for emphasizing that or being opinionated. I think with Rust that attention results from its choice of memory management model which gets in the way a lot in ways described in the article.
> 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
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.
You were flagged for a pointless quip about "woke"ness. Other people repeated more civil and reasonably argued forms of your same point about the language and its community and received no such downvotes.
If you blame the wrong reason for downvotes, expect to be corrected. Your complaint here is invalid.
Try criticizing Rust next time, if you want to demonstrate that criticizing Rust gets downvotes. You were throwing out insults in your very first comment.
I'd go read their mailing list and Reddit forms; especially when people run into issues doing stuff that's very simple in other languages. Never seen a more toxic programming community.
Hopefully they calm down, or really get drown out, once there are a real number of jobs for people using Rust. Right now the evangelists outnumber the rank and file who are just using a language to get work done.
If it helps, they can't possibly be as toxic as Lisp programmers used to be, where more or less any online conversation would start with someone new asking a question and Erik Naggum replying that they were a moron who should die.
Probably because there's so many more of them. Maybe because being called not a real UNIX programmer feels different from being called a Blub programmer.
Maybe I should ask: why should someone interested about Lisp today have to hear stories about some Erik Naggum who posted to a Usenet newsgroup, and died 15 years ago?
Let's assume that the newsgroup is important. Legendary Lisp hacker Alan Bawden posted there just last week or so. Nobody ever mentions him.
Every other one I've met in my life was nearly as unpleasant. Fewer death threats but they clearly all thought they had 200 more IQ points than you because they could write a macro. Thus the term "Lisp weenie".
In this case I think people should learn from history and that specific examples are the best way to do that. It's, like, effective pedagogy or whatever.
I'm active on both and have not seen this behavior.
In fact, my experience has been the polar opposite, the rust community has been very friendly and accepting of critique.
So again, I'm going to ask for an example of rust language fanatics frothing at a criticism. If it's such a community problem this should be easy to find correct?
Here's the OPs article on /r/rust and it's both got a fair number of up votes and the top comments are all really positive towards this article. That's what I've seen at typical in the rust community.
It may not be flaming, but the author brings up a particular quote repeatedly. "You just don't get it/have enough experience with it yet."
I've seen this everywhere. This is an obnoxious, lazy thing to say to someone. It's a go to for many "enlightened" languages that have small ecosystems and something to prove. The only response is to ignore it entirely or, like the author did, dedicate years of your life just to see if there's something to it. This is not okay. Life is short, and we lean on other developers experience to keep us from wasting our time.
If someone posts a topic wondering if X language is bad for something, it's an earnest question. Not a time to flex your dedication to the cause.
Based on what I've seen, various forms of censorship and suppression are often employed in such cases, rather than outright "flaming" or other discussion-based approaches.
It really depends on where and how the discussion is taking place, and what censorship methods the website/platform/medium involved offers.
Sometimes users are just outright banned or shadow-banned, if those happen to be options.
Sometimes forum threads, bug reports, or comments are deleted.
Sometimes the discussion remains accessible, but is stifled in some way. This includes closing/locking forum threads or bug reports, or otherwise severely limiting participation in such discussions to a very small and isolated group of people. If down-voting/reporting systems are present, sometimes they're used to limit the visibility or prominence of such discussion.
I haven't rigorously tracked all of the instances I've seen of this happening over the years, but I've tried to quickly find some more prominent examples for you.
This bug report, for example, has various "This comment has been minimized.", "rust-lang deleted a comment from ...", "rust-lang locked and limited conversation to collaborators" interference:
When Rust is discussed here, it's common enough for reasonable and relevant Rust-related comments to be voted down, sometimes severely. These threads have some examples I quickly found via a search of high-activity Rust submissions:
Here's an example of a recent submission on this site for an article very reasonably and thoroughly questioning Rust. It got some attention, and now it's currently marked as "[flagged]":
Keep in mind that strict "moderating" (ie, censoring) has been an integral part of the Rust community's identity for many years now via its Code of Conduct and Moderation Team -
I think I just read about 10 versions of this comment on this page, and definitely not a single response to the criticism that could be described as incendiary. I don't think I even saw a single comment just now that fundamentally pushed back on the premise of this article, let alone in an incendiary way. It's early yet, and maybe this thread will look very different in a few hours though?
The author maybe somewhat hit on the reason for this in the article, where they mentioned that they're already seeing some of the rabid, toxic, Rust proponents already moving onto the next "hot" thing and doing their thing there. So maybe after a few years of Rust we've arrived at the turning point now where enough of those types of people have finally moved on and the Rust community has significantly changed.
I don't think that's the case. N=1, but I'm usually a quite staunch, and occasionally incendiary, proponent of Rust, because the arguments against it / criticisms of it I usually see seem fundamentally misguided or even disingenuous to me — whereas in this thread, I've been only agreeing, because the criticisms are fair (I agree Rust isn't built for, and is quite bad at, prototyping, fast iteration, flexible code, etc), if I think a bit overblown (I think many of the patterns the author complains about being forced to use like command lists and generational arenas are very good). That could be the difference you're seeing, IMO.
Perhaps because the article is about how Rust isn't the magic bullet to everything, and a few people have commented agreeing with the article, others feel more willing to comment their own Rust isn't perfect opinion as well.
If you go into the comment section of a pro-Rust article, where the first few top-level comments are also pro-Rust, the responses to people expressing a negative attitude about Rust tend to (in my experience) be different.
This phenomenon certainly isn't exclusive to Rust (or HN). It happens all the time, especially when a prolific commenter is among the first few comments. It can set the tone for the entire comment section.
I assure you it happens, but the people targetted this way usually quickly learn what is ok and what isn't to say, especially on rust's reddit. If you wanna see examples, look at my reddit profile (same username). I dared to say bevy was full of hype and false promises and tat the money they get would be better spent elsewhere. And look at the hate i received.
One way i've seen to reduce this is prefixing any posts with "I am not criticizing any engine in particular" even if it's blatantly obvious because the criticism only applies to one.
I guess I interpreted the comment as meaning that the incendiary responses were going to be seen here. I would expect incendiary responses to anything I post on reddit...
This is a textbook example of poisoning the well. [0] We see it used in every discussion about pros and cons of a language on HN.
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.
This article describes almost exactly why I think gradual typing is actually a good thing. Type checkers shouldn't get in the way of your code compiling. Yes, the language has to be designed with this property from the beginning. Yes, you should always enforce complete checking in CI. But you should also be able to try half-baked ideas.
There are at least a few nascent statically typed languages (as in, full static typing rather than gradual) which nevertheless let code with type errors compile for the sake of testing.
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."
> Type checkers shouldn't get in the way of your code compiling.
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.
Because often, and especially with heavily interactive programs like games or UIs (any web page), you don't know if something will be good or not until you build some working version of it. The more barriers there are (type checkers, compiler errors, etc), the longer it will take you to prototype something usable to check if what you're building is good.
Sometimes, it's useful to bypass these things for a prototype and you don't care if it crashes on any edge case. This is why typescript is so popular on the web - you can quickly prototype something with JS, then once you find the right solution, add types and productionize the code
Exactly this. Typing in the final version is great. Typing up front is almost always getting in the way.
Most interesting programs involve a lot of figuring things out as you go, and so any "tax" on that process is one you hope to avoid. The last thing you want is for the language itself to be the source of that tax any more than it has to be.
I agree with this, but I don't think dynamic types is the only solution. Something like Roc[0] strikes a better balance: it gives you a flag for development, and when enabled, all compilation errors become warnings. The compiler substitutes every function it couldn't compile with one that panics at runtime.
But a type error isn't an edge case! It means you've written something the compiler can't understand.
> This is why typescript is so popular on the web - you can quickly prototype something with JS, then once you find the right solution, add types and productionize the code
I think you've got it backwards - TS is popular because people want to use types up front, but it has to work with JS, which is so dynamic that it's impossible to write a sound type system that can even touch it.
I don't want to reply "git gud" but I really am struggling to understand how people are writing code that where it is so difficult to use type information or where changing it is so cumbersome that you think it's a barrier. And I don't see many games or performant GUIs written in dynamically typed languages, particularly outside the web. Even in very dynamic languages like Objective C, things are still well typed.
> But a type error isn't an edge case! It means you've written something the compiler can't understand.
I get it! But the compiler here is getting in the way, so Rust is the wrong tool choice here, or for anything that requires quick prototyping (like the OP said)
> writing code that where it is so difficult to use type information
It's not difficult, it just takes longer. Typing up front works well when you know exactly what you're building. When you don't, or are doing experimental design, they just get in the way until you've settled on a direction. This direction is not due to technical constraints or language choice, it's simply that designing complex user interactions is hard and you don't know it's correct until you have users use it
> dynamically typed languages
Because the cost of prototyping in a dynamic lang and then fully rewriting in a performant language is higher than having slower iteration speeds in typed languages. But also, this is why a large number of new non-web GUIs use electron (or are mac exclusive, which offers other benefits for GUI development)
> I think you've got it backwards - TS is popular because people want to use types up front,
Personally, I mix it up. There are things I know what their types will be no matter what so i add them up front. Then there are other things which I do not know, and I add those types at the end once i'm performing the final cleanup before code review
The nicest part is, TS runs regardless. I often refactor my types frequently since I get them wrong a lot (especially the up front ones) and have TS in a failing state while developing, but the TS compiler doesn't get in my way, and still works for the types that I expect to have working
A type error means a type error, and nothing more. Type errors do not mean "incorrectness." Here is an example of something you can't do in rust, but can do in other statically typed languages.
```rust
struct Type1 {
id: u32
}
struct Type2 {
id: u32
}
fn main() {
let obj1 = Type1 {
id: 1
};
let obj2: Type2 = obj1; // compile error, Type1 is not Type2
}
```
This code works perfectly fine. These two structs have the same signature. The only thing that doesn't work is the names. There are dozens of things like this where your code works but the compiler is too strict. This is terrible for rapid iteration. It's good for other things like modeling your domain with types.
This was just one example of type error != logic error.
> one is for things that are structurally identitical but semantically distinct where confusing them creates a semantic error, the other is for alternate (usually, more concise) names for an existing type
This is not always true. Sometimes there are cases where you have different type aliases and want to use them interchangeably. Now you have to refactor them to inherit from some general type that didn't exist before. I also could count on one hand the amount of times this has caught a genuine bug even with diligent domain modeling (Which I would argue is overhyped).
>I don't get it, what's the point of type checking if not to reject invalid programs
It can take longer to think about how to properly type instead of just writing some code, testing it out, and immediately seeing if something is wrong. You also often get into a situation where linters who like to act like type-systems give you arcane errors (looking at you TypeScript).
In the moment I just want to move some data around and quickly try out an idea, I don't need it to be resilient or perfect.
> Because not every program that doesn't compile is necessarily invalid.
I think that more programmers should be aware of this point. Rust doesn't reject code that will crash, it rejects code that it cannot prove won't crash.
The code being rejected might be just fine (as numerous examples in the article showed).
>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.
The article addresses this, multiple times. In brief, the "poor structure" isn't the problem.
It's also pointless to care about types in fine-scoped code. Like if your language forces me to assert that i is an integer like this, something is wrong...
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.
All low-level languages (by which I mean languages that offer control over all/most memory allocation) inherently suffer from low abstraction, i.e. there are fewer implementations of a particular interface or, conversely, more changes to the implementation require changes to the interface itself or to its client [1]. This is why even though writing a program in many low-level languages can be not much more expensive than writing the program in a high-level language (one where memory management is entirely or largely automatic), costs accrue in maintenance.
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.
Almost all parallelism in game engines is for very specific parts of the engine and almost none of the gameplay stuff is paralellizable. What people who haven't actually had to go through and solve the problems presented in game engines often times misunderstand is that when your game is running poorly because everything is happening on a single thread, almost all of this speed issue is because of rendering. Then physics. These are very hard problems to solve and it's more complicated than "use an ECS" to solve them.
> almost none of the gameplay stuff is paralellizable
Define gameplay. If you have some simulation happening as part of the gameplay, parallelizing it can be quite useful vs killing one CPU core on it. Physics is just one common example, but not everything is about physics. You can simulate whatever.
Thinking parallel is heavy mental load to carry even if rust gives you the tools to make the load lighter.
If one or two threads(game loop & rendering threads) is enough why increase the difficulty by going even more concurrent, for little to no extra payout.
I had this same pain point though when using Rust that was meant to run in the browser (compiles to WASM), i.e. guaranteed to be single-threaded.
Still had to pay the very high price of accessing global state in Rust.
Ended up moving that code to JS instead just to avoid that!
> Making a fun & interesting games is about rapid prototyping and iteration, Rust's values are everything but that
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.
That's a good example. I actually think dot syntax is really under-utilized sometimes. Although personally, I'd prefer that if v was a reference/pointer to a struct, that v.b simply performs a pointer offset, instead of auto-dereferencing like ->.
I agree, and D with the GC lets me prototype quickly. Its type system gets progressively stricter with constraints, for code that survives. I wouldn't want all the type system to apply to prototype code or a nascent program.
The power of attributes and get/set in languages like C# is similar. It's easy to turn value types into functions. Or to move a value into a subclass and you don't need to do a.b.c (moved c into b) because you can add c => b.c to the a class.
Gaming is C++ first and foremost. All other languages suck, except when used to script game engines (C# in Unity, etc.). There's no practical reason to choose Rust or anything else. I don't think Rust is particularly bad or good here. There's decades of work to catch up on. I don't see Rust becoming a truly great language for games unless it's blessed by Epic or Unity.
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.
Exactly. Bevy has the advantage of being built using the "right way" from the start. This makes an enormous difference in the ease of hacking on rendering code.
Ironically, the main thing holding Bevy back is the bickering at the W3C. WebGPU is still not widely supported, so we have to support WebGL 2 (with reduced functionality in some cases), and that adds a lot of complexity.
> Bevy has the advantage of being built using the "right way" from the start
I think the article is precisely criticizing this type of comment... You make people believe that Bevy is some kind of safe bet for the future, that it took inspiration from the greatest to build even better foundations for a game engine... And it seems common in the Rust community: make audacious unverifiable claims to enroll other "believers". But it's easy to claim when the tool itself has a fraction of the functionalities of Unreal, Unity or Even Godot. Heck, last time I used it (about 2 years ago) there wasn't even any built-in physics stuff. You have to install plugins from every corner, some require old Bevy versions, some other require newer versions... It's seriously unusable to just "get things done"
Well, there are two types of game engines from what I've seen:
- One where most things are done out of the box, and you can mostly GUI visual code to success - these are your Unreals, your Unitys and Godots.
- Second, where most things need to be built piecemeal, and many things are missing, and need to be built - Bevy falls into this category, along with stuff like PyGame and what not.
I mean, Bevy is a fine engine for small things, but I can't admit I've seen any indie game of more renown succeed with it. OTOH, doesn't mean you can't make it, but it's definitely more effort.
Well, I don't call the second ones "game engines", I call them game libraries, or game frameworks. RayLib also is a nice one. But they don't claim to be "engines" (or at least, they are mostly described with the aforementioned terms)
I don't think anyone ever claimed that Bevy is finished.
As for audacious unverifiable claims: it's quite verifiable that Bevy never used DirectX 9, DirectX 10, shadow volumes, HLSL, Cg, Vulkan, or Metal (I'm not sure about OpenGL or GLSL; those were before I got involved with the project). It chose the better path (wgpu) early on. That's all I was saying.
I have about a half dozen PRs open, plus more on the way, that will get Bevy's renderer up to rough feature parity with Godot (Godot will have some things Bevy doesn't, like SDFGI, and Bevy will have some things Godot doesn't, like deferred and GPU occlusion culling). I have no doubt in my mind that Bevy's design is solid, but if you're skeptical, we will find out very soon :)
I'm not a pro engine dev, but from what I've heard, the graphics backend is a minor part of what makes a good and fully-fledged game engine. I see it more as a strength than other engine can ship on older and more legacy hardware. Moreover wgpu is still in its infancy but evolving rapidly. We'll see in the future how bevy will develop, and I really hope it works out, because I liked some of the ergonomics of the ECS when I used it, but for now I am very doubtful
Well, the example code for a simple button referenced in the article has insane complexity that has nothing to do with WebGL though. It's precisely "the right way" that adds a lot of complexity.
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.
I'm a Rust fan (mostly for embedded firmware with minimal deps), but even after 10 years of playing with the language it's not clear to me that advanced GUI or gamedev fits well with the borrow checker. It requires a significant paradigm shift in architecture, and I'm not convinced it's worth making that shift, especially if your application can tolerate a garbage collector (which many games and most UI apps can).
I hope Rust does gain mature options for its GUI ecosystem, but the author of the article makes a very good point that in other languages, there would be mature options in use already. "Seems promising" is too little, too late.
Development speed is many times lower than with Typescript frameworks, while the result is not faster or significantly more stable.
Why should anyone choose Dioxus over Sveltekit, Next or Nuxt? I never had an issue with a frontend app that the borrow checker would have catched. Error handling was an issues some years ago but is solved by now when using one of those modern frameworks. (I don't know if Dioxus has error boundaries, though.)
Those Rust fullstack frameworks make sense only for people wanting to use Rust, not for people looking for the right tool for the job.
Agreed. Multiple languages exist. They can be part of {your, your team's} toolbox for different specific purposes. Some languages are set by other tools or by team members' backgrounds. Popularity also lends itself to greater availability of tools and Q&A forums. In the end, it's a better decision-making process to select what is most likely to be long-term productive for a specific project and team.
The godot-rust project crates take a minor amount of adaptation to understand how it exposes the Godot object system in Rust but it's also pretty well developed.
Last time I tried Godot with C# in Visual studio, when I debugged I could not see the console output, and when I ran with the console output I could not debug (the breakpoints weren't hit). A Google search later and turns out it wasn't just me.
Godot C# works pretty seamlessly with VSCode and has improved dramatically over the years. It did regress a bit in Godot 4 after swapping to the newer .net "core" (in terms of platform support) but as of 4.2 I have had no issues at all.
Now I'm wondering how far people could go a hypothetical Rustscript* that transpiles to Rust (or hooks into rustc?), introduces extra features such as reflection, removes lifetimes, and changes the defaults around things like monomorphization.
If you're removing lifetimes from the script, I'm not sure how you're then transpiling to Rust, unless you wrap everything with reference counting, at which point you're better off using a language with GC.
I would assume that it would opportunistically try to run the borrow checker, and if it fails on the access of a specific field, turn that access into an Arc/Rc everywhere, leaving any other access as references. This leaves you with invisible performance cliffs, where accessing a field in a new place suddenly increases the cost of accessing it everywhere else, but it does give you the "just do what I want, damn it!" development experience. I doubt Rust itself could do that without alienating its current userbase, but a RustScript could.
We're doing more and more of our back-end work with Rust. The main reason is the performance it provides. It's not just great for our end-users it's also so much cheaper in the modern world where we pay per mileage in the cloud. Part of what we really like about Rust, however, is actually exactly the variable ownership because it makes it very straight forward to enforce and control data-integrity and avoid race conditions. Even for programmers who would struggle to do so in C or C++.
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.
Rust ain't Go but anything Go has can be used as an argument that Rust should try to do better in certain areas. ;)
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.
> 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.
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.
> but the ability of engines like Bevy to analyze system dependencies and automatically scale to multiple CPUs is a big deal
Is it? The article addresses that, and basically calls it a pointless feature that is almost never used and when it is the benefits are mostly lost because of real world needs and constraints, and that the problems it solves are easier solved through other solutions and add-on systems that are well understood.
I think this might be a case where explaining the real-world benefit instead of the theoretical benefit is needed, if only to counter what are very pointed criticisms that are definitely deeper than at the theoretical level.
You can see that all the CPUs are being maxed out. This actually does result in significant FPS increases. Does it matter for every game? No. But it does result in better performance!
The problem is that most of the gameplay code is linear, and people have already gotten good at splitting parallel work across threads. Serious physics engines (see jolt) are already designed to run on another thread and distribute the work across multiple cores. The main part of graphics drivers when using opengl or vulkan run on another thread and the UI you access just passes data to it. Rust's parallelism hasn't proven to be faster than C/C++, let alone less annoying to achieve.
Among those who have tried both, I can confidently say that the idea that C/C++ parallelism is as easy to achieve as parallelism in Rust is very much a minority view. There's a reason why nobody tried to parallelize CSS styling in a production browser before Stylo came along.
I've parallelized emulators in C++ and work on parallel parts of Bevy now, which is probably the closest you're going to get to someone who has worked on parallelizing parts of large game engines in both C++ and Rust. It was far easier in Rust.
>> but the ability of engines like Bevy to analyze system dependencies and automatically scale to multiple CPUs is a big deal
>> Is it? The article addresses that, and basically calls it a pointless feature
> You can see that all the CPUs are being maxed out.
You're missing the forest for the trees - the poster above basically said "seeing all the CPUs being maxed out is a pointless feature" and you reply with "but see, all the CPUs are being maxed out".
You're literally ignoring the complaint and replying with marketing.
No, the original article said that you don't get parallelism from Bevy in practice:
> Unfortunately, after all the work that one has to put into ordering their systems it's not like there is going to be much left to parallelize. And in practice, what little one might gain from this will amount to parallelizing a purely data driven system that could've been done trivially with data parallelism using rayon.
It's not saying "yes, you get parallelism, but I don't need the performance"; it's claiming that in practice you don't get (system-level) parallelism at all. That's at odds with my experience.
To be fair, you've posted a toy example. Real games are often chains of dependent systems, and as complexity increases, clean threading opportunities decrease.
So, while yes it's nice in theory, in practice it often doesn't add as much performance as you'd expect.
The article is not saying that Bevy does not parallelize but that the impredictability of parallelism (both in ordering and in timing) forces the developer to add enough dependency constraints that there is not much left to parallelize.
I'm saying that making it easy to experiment with different gameplay mechanics is far more important than making it the most efficient. Even more so in case of small studios.
The context of this article, and my comment, is game development. Not game performance or engine optimization, which while related seem like related but smaller aspects of the overall topic.
The way I interpret the claims is that bevy is putting far too much focus on performance and multi-threading when by far the important thing to focus on for game development is allowing the actual game developers to rapidly iterate.
Bevy might be very fast and performant, but if that seems to have come at the cost of (or been optimized for over) features that make it easier to use in ways that game developers need, then the criticism may have merit. Whether that's true or not I don't know, but hopefully that explains why a response about how it definitely can use lots of threads and make good use of many cores isn't really seen as a good rebuttal to the criticisms leveled.
It's a good feature, but still a niche one. It's a bit like choosing Unity only because of DOTS. For a few projects perhaps it make sense. But just a few ones.
Nobody said that every game needs that level of performance. But saying that it's a solution looking for a problem is not true.
I'm fully in favor of having Bevy support dynamic languages, as implemented in for example bevy_mod_scripting [1], for projects that don't need that parallel performance.
Good scripting support is probably the way to go for Rust game development anyway in order to achieve high iteration/idea testing velocity. We could have a script engine that memory-manages various in-game objects and the scripts call into Rust functions to do the heavy lifting. Those Rust functions will typically take things by reference from the script engine so that memory-management is mostly a non-issue.
This might be controversial, but "Safety" and "Speed", in the same ecosystem, are not free. The cost is heavy syntax and heavy cognitive climbs. Why Rust was ever sold as a language for the masses is beyond me. A safe, fast, hard language is something you use for operating systems, aircraft, etc.
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.
> 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.
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?
> So.... Sounds like memory safety is indeed a problem? Otherwise why do so many solutions exist for it?
Memory safety and memory management are different things. Scripting languages remove the burden of manual memory management; as a side effect, they also tend to be memory safe, but that hasn't been the main motivation.
As I said in my own comment down thread, despite being a huge rust advocate, I sincerely agree with you here.
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.
> Rust game development feels like a solution looking for a problem to fix.
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.
This is not necessarily a bad thing. Especially given that Rust is an immediate upgrade with no downsides when moving away from C or C++. It is easy to see with people never wanting to go back, which also involves getting companies and products to adopt it as you would otherwise be forced by the market to work with inferior tools.
As a counterexample, .NET suffers a lot from the lack of evangelism - big chunk of community that started out back in .net framework days still thinks of it as poorly as people outside the ecosystem because they never bothered to drop old and obsolete tools and targets and give new versions a proper try (as the code is often vastly simplified and performance is vastly better).
Other programming languages, not only Rust, also do better at self promotion - take for example Go that managed to convince everyone to put it in the same bucket as Rust (which, personally, I find absolutely insulting as C# is a much closer alternative to Rust both in performance, features and access to low-level bits).
I mean, if we are allowed to lie in order to promote Rust, why don't we just smear all the C/C++ code bases in the world as security hazard needed to be sorted out ASAP?
I doubt security is the matter everyone is concerned with but rather the quality of tooling and developer experience.
It is, of course, difficult to convey to developers who only experienced C and C++ build systems, or Ruby tooling and brittleness, or Python way of managing dependencies, or setting up the packaging when using Java, that fast and easy to use solutions do not come from trade-offs but from just better ways of doing so - using cargo and Rust or dotnet and C# is night and day difference compared to options listed above.
I said it here in the past and will say it again: it's not that Rust (or .NET for that matter) are that good, it's a lot of other popular languages and platforms are that bad at one or another aspect (or many at the same time), that make it sufficiently painful to never tolerate a downgrade when you worked with a tool that offers better all-around experience.
I value good tooling as much as the next software engineer. We have good IDEs, build systems, package managers in Java and .NET lands; but we also have a decent environment of established, well-maintained libraries and frameworks.
Rust is deemed to have good tooling, but the third-party library ecosystem is following the NPM/RubyGems culture with all the fragmented dependencies, plus the added complexity of compile times due to lack of ABI compatibility.
Meanwhile, monolithic projects like Tokio also keep strengthening their reign among the small peasant crates.
I'm learning Rust, after decades of various languages with garbage collector, and I believe in the language itself and its tooling. But everything else about Rust irks me.
I find that Jonathan Blow ranting about Rust game development here https://www.youtube.com/watch?v=4t1K66dMhWk. He adds interesting perspectives to the discussio, how the language makes the Rust game developer resort to arrays and their so called Rust point of views.
Well the problem with Jonathan's argument here is that he's spent the past decade mostly ranting about Rust and working to make a perfect game programming language, instead of making games.
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 mean, it's not like there are no games in existence that shipped with a custom engine.
Even in hindsight it's hard to judge whether building your own engine was good or bad decision, and we are nowhere near "the hindsight" level of knowledge.
Last I heard, the Sokoban game has a ridiculous number of puzzles on it. Can't find a source but I seem to recall hearing that it would take 400+ hours to finish it all. So.. I don't think it's entirely unreasonable it's been taking this long.
I mean, he does like a good rant lol. But this seems like a bad take. The witness came out ~8 years ago, and Braid came out ~8 years before that. Braid Anniversary is launching next week, he's actively developing his language and next game (occasionally streams).
"he's just resting on his laurels now" I think is clearly wrong
>spent the past decade mostly ranting about Rust and working to make a perfect game programming language, instead of making games.
I wish. As far as I can tell he made a single hour long video shitting on rust and now he's the enemy of the cult. That's hardly spending the last decade sitting being mad over rust.
The blog post reminded me of a quote from Jonathan Blow as well. I forgot the exact wording, but basically he said that Rust makes you treat every state of the project to be production ready (e.g. free of memory safety bugs), but in game development, most of the time the project needs not to be production ready, and for a good reason (rapid prototyping). You just have to fix the really bad things (crushing bugs) before shipping.
This is missing a few useful ones, like Box<str>, Arc<str>, Cow<'a, str>, SmallVec<u8>, transmuted newtype references like &UserId, and of course the string type you implemented yourself because the previous ones were not good enough.
Someone who has experienced real problems as a result of a specific mechanism is not required to solve every single problem with alternatives to that mechanism before saying "this mechanism has caused me real problems and it'd be nice if there were a better alternative that didn't cause those problems".
> The moment you allow this, you have to find a way to pick between several implementation - and they don't always have sane names.
There are other possible solutions that don't involve that.
For instance, many applications would be quite happy with "There can be only one implementation", giving a compiler error if there's more than one.
A slightly more sophisticated rule would be "Identical implementations are allowed and treated as a single implementation". This would be really convenient in combination with some kind of "standalone deriving" mechanism, which would generate identical implementations wherever it was used.
Disclaimer: I'm aware you guys are working on relaxing orphan rules, and I wish you the best of luck. But as an outsider, orphan rule doesn't seem to be going anywhere soon.
And if the original poster had said that I would be ok. Instead what they said is:
> It's a great example of something I'd call "muh safety", desire for perfection and complete avoidance of all problems at all costs, even if it means significantly worse developer ergonomics.
This implies the writer didn't assume what happens if you "turn-off" orphan rules. I.e. you don't trade perfection for developer ergonomics, you trade one set of developer (ability to write any trait for any type) ergonomics for another (having to battle two incompatible trait implementations from crates you don't own).
Either you have to manually specify how nearly every implementation is pulled (horrible developer ergonomics) or, even worse, you go into monkey patching territory.
> For instance, many applications would be quite happy with "There can be only one implementation", giving a compiler error if there's more than one.
Ok. But you still need a resolution mechanism to fix the error. Which implies manually solving ambiguity. And how do you solve it for blanket implementations?
Say by transitive dependencies, alpha_v0 and beta_v0 are imported, and due to vulnerability, you upgrade beta_v0 -> beta_v1 (let's assume that trait is essential for the vulnerability resolution).
Now what? You either have to skip future updates, or keep the vulnerable beta_v0 crate.
Yes, that's the downside of relaxing the orphan rule. That doesn't mean there has to be a way for the top-level crate to work around that, other than avoiding having both of those in its dependency tree.
Ideally, people would tend to put trait implementations for a given pair of crates in a unique library crate, implement them in the obvious way, and provide that as a library for people to use.
> This implies the writer didn't assume what happens if you "turn-off" orphan rules
It implies that the writer would prefer using a different language, not that Rust would be better if it was all the same but with the parts he doesn't like taken out
> It implies that the writer would prefer using a different language
That's not what they wrote.
> There are mostly valid reasons for wanting the orphan rule for things such as libraries uploaded to crates.io, and I am willing to concede that crates published there should obey this.
> But I have a very hard time caring about this rule for applications and libraries developed in end products. I'm explicitly not saying binary crates, because most bigger projects will be composed of more than one crate, and many will be more than one workspace.
It talks explicitly about Rust. In hindsight, the author might have been better suited by another language, but expecting Rust to suddenly become another language is weird.
Author here, sorry about that, I just deployed a fix, should be readable now. If it's not, here's the first few points
- Once you get good at Rust all of these problems will go away
- Rust being great at big refactorings solves a largely self-inflicted issues with the borrow checker
- Indirection only solves some problems, and always at the cost of dev ergonomics
- ECS solves the wrong kind problem
- Generalized systems don't lead to fun gameplay
- Making a fun & interesting games is about rapid prototyping and iteration, Rust's values are everything but that
- Procedural macros are not even "we have reflection at home"
- ...
the list corresponds to the titles of sections in the article.
1,000 comments
[ 119 ms ] story [ 730 ms ] threadIt 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.
Is the orphan rule a result of some technical limitation? Or just the idea that it's "unclean" to implement someone elses traits for someone elses types?
Haskell went through this as well. Orphans used to be allowed and I certainly saw their appeal.
The problem is that the compiler might see two different implementations of ToString for MyType in different source files. The compiler could probably make a check for that if it were compiling both files at once, but if you want to be able to compile source files separately and only recompile files which have changed, etc., I think it gets harder to spot.
> someone has to hack around that with a newtype wrapper
Don't think of it as hacking around it. It's the blessed approach. Newtype wrapping is giving a proper names to the behaviours, so that they don't get mixed up.
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.
(can only assume you're joking)
I find I can move between dynamic languages and Scheme pretty easily.
I love elixir because it feels like the syntax clojure would have if it had a syntax imho.
I think 'everything is unergonomic' is a too strong of a statement to be true.
I'd argue that lisp is very ergonomic for those who work with it regularly (and are into using Emacs). It sounds like you are beefing with the syntax (or lack thereof) and homoiconicity of Lisp. I can understand that. It is a very different language than others currently in the mainstream for that reason.
As far as overall ergonomics, I'd say the REPL/image-based development style and the macros that are enabled due to homoiconicity actually make it one of the most ergonomic languages in existence.
The biggest thing that prevents Lisp-likes from going mainstream is that it is too ergonomic, specifically, when you start reading a Lisp codebase, you essentially are signing up to learn a new project-specific dialect. Very ergonomic for writing code, but at the cost of understanding how that code operates.
Generally Lisp has two main goals:
* allow code to be data and data to be code
* make coding and coding code (let code manipulate code) interactive
The s-expression based syntax was found to be useful for both. For the latter it means that code can be manipulated interactively for example by structure editors or in read-eval-print-loops which work with data. That makes interactive code writing very ergonomic.
That is simply false. The ergonomics of editing Lisp is also superb.
There is a consistent, logical way to break any Lisp expression into any number of lines of text. The more you break Lisp into multiple lines, the more clearly the tree structure of the code is revealed.
The absence of ambiguity helps readability: not having to guess which expressions are children of what operator.
Lisp code sometimes makes up for the parentheses by omitting superfluous punctuation like commas and semicolons. To add two terms, we need ( ) and +. But that's all we need to add 17 terms also.
Imagine if the Unix shell required commas between arguments:
that's how Lisp programmers feel when back in a non-Lisp.The more usual Lisps (I consider Julia, like Dylan before it, to be a Lisp) give up most of the advantages of syntax in exchange for an admittedly eloquent macro system. Julia gets the advantage of syntax, and a macro system which is of equal expressive power but less easy to use. Also, in Julia, a macro leads with `@`, the way a macro in Rust ends with `!`, meaning you can read the difference between a macro and a function at the call site. I consider that a good thing.
https://iiif.library.cmu.edu/file/Simon_box00075_fld05959_bd...
I dabbled in some game dev in CL and loved it. I think the ideal would be low level engine, lisp on top driving it. If you could do it without the FFI barrier slowing things down a crazy amount, it would be the killer combo.
And it actually does work that way with Haskell, in my experience. There's a big hill to get over where you flail against the type system, IO monad and all, and then you realize that, while Haskell's type system isn't perfect, being able to say
is really pretty powerful, and being able to search for functions by type signature is just plain convenient.But Haskell isn't for every problem in the way Rust is apparently made out to be. For example, I've seen some posts about developing games in Haskell, but it isn't common, and nobody would try to push a game developer into using Haskell.
[0]: https://en.wikipedia.org/wiki/Else_Heart.Break()
- https://wiki.haskell.org/Applications_and_libraries/Games
- https://wiki.haskell.org/Game_Development
- https://hackage.haskell.org/packages/#cat:Game
- https://hackage.haskell.org/packages/#cat:Game%20Engine
- https://haskell-game.dev
- https://www.reddit.com/r/haskellgamedev/
- https://www.reddit.com/r/haskell/comments/z98ubk/defect_proc...
Re the original post: This was a brilliant writeup with lots of insights for Haskellers as well. Thanks!
It's certainly not all the way there, but now that we are over the mostly empty promises of encapsulation and (worse) trying to model the world with classes, but grouping by that implicit first argument type for discoverability is exactly what OOP is, in this age of post-OOP.
The "you're not trying hard enough" position reminds me a lot of the Scala community's disdain for java-in-scala-syntax: if you don't use it like an almost-Haskell you're on your own (1). Here, I think Rust is actually more open: I don't read too much of the community, but most of what I saw seemed to be quite welcoming to the idea of placating the borrow checker with refcounting when "the rust way" fails to sufficiently click.
((1) but with Kotlin having taken over the entire not-almost-Haskell part of scala it's not an issue anymore: is has its niche and it fills it well, no more taking over the entire JVM while also magically converting everybody into an almost-Haskellian)
But this article is seeming about someone that has put in the time, has written a lot of Rust, and now says, it wasn't really worth it.
They both have a single odd idea that's hard to wrap your head around at first, Rust's borrow checker and Haskell's type system with its enforced immutability by default, and which intentionally drives program design, in that both languages demand you write your program such that the static analysis can prove it's valid, but Rust's borrow checker seems to get more people throwing their hands up and leaving the language than Haskell's type system does.
Haskell's type system can be a bit inexpressive, sometimes, too, and there are language extensions which address some of this, but it seems to hit more of a sweet spot where, in most programs, if the type system is forbidding something, you really do have some kind of error in your thinking leading to inconsistent code. Plus, it enables design instead of just forbidding, because you know that any function which is valid in the type system is at least sensical to use in a given context.
There's something about the new hot language/framework/paradigm that always attracts the type of people who wrap their identity up in the hot new thing. They take any criticisms of their new favorite thing as criticisms against themselves, and respond defensively.
What I see now in certain Rust communities feels a lot like what I saw in Golang communities in the early days. I had similar experiences when React was the new kid on the block, too.
Some of these communities/forums are just inhabited by people who thrive on dunking on noobs all day long. The good news is that they tend to get bored and move on when the next new thing comes out.
I'm already seeing this in my local Rust community as the chronically online people are moving on to Zig now. They even use one of the Rust discords to advertise their Zig meetups and proselytize about Zig at every opportunity. Eventually they'll get bored and move on.
> > 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".
I don't fully agree with this assessment. In my experience, Rust community members who arrive with well thought out complaints or suggestions are welcomed by the people who like working on programming language fundamentals. You can nerd snipe a lot of Rust enthusiasts by showing up with a difficult problem involving tricky parts of Rust, which will often result in some creative solutions and discussions.
On the other hand, Rust communities are inundated with people trying to write Rust as if it was their old favorite language. I've spent a lot of time trying to get people to unlearn habits from other languages and try to adopt the Rust way of doing things. Some people refuse to let go of paradigms once they've used them for years, so doing things the Rust way isn't going to work for them. I've worked with enough people who spent more time fighting against Rust than trying to adopt Rust ways of doing things that I've also reached the point where I don't engage once I see the conversation going that way. Can't please everyone.
I would say that in general, this type of attitude permeates a lot of software engineering, and even engineering and science as a whole. When I speak with people in other fields, particularly more creative ones, the discussions are so much more improvisational and flowing. Discussions with software developers, engineers, and scientists have this very jerky, start and stop "flow" to them as you meet resistance at each step. I honest to god have had people telling me no or shaking their head no before I ever finished my question or statement, much less before they even understood it.
> There's something about the new hot language/framework/paradigm that always attracts the type of people who wrap their identity up in the hot new thing. They take any criticisms of their new favorite thing as criticisms against themselves, and respond defensively.
You're spot on about the coupling of someone's identity with it. Rust especially seems to also have this never-ending gold rush to be the next framework and library everyone uses, which creates a very competitive landscape. And it seems most frameworks and libraries get 80% of the way and then fizzle out once people realize Rust isn't a magical language that solves all your problems.
This is the opposite of creative and optimistic conversation, where you break boundaries between things to create something new.
The attitude to be analytical in conversations comes from the fact, that software engineers usually do creative part of their work alone, and use communication for analytical part (code review, requirement clarification, etc). For some creative people it's natural to do creative work together, especially in music, so it's easier for them to adopt a creative attitude in conversations.
Go was released in 2012, Rust in 2015. Are you saying we are still in the early days of Rust?
C was invented in the '70s and only got standardized 20 years later.
And Rust's ~20 years is young in systems lang terms (the alternatives, C and C++, are 50 and 40 years old).
And nobody had TikTok back then.
huh? It's only been about 9
Release dates mean very little. Golang had rapid adoption early on. Rust only recently became one of the fastest growing languages. Golang stabilized a lot of things about the language and library early on. Rust still has a lot of common features gated behind nightly.
There is a fun tool for comparing the number of PRs on Github by language that shows the difference: https://madnight.github.io/githut/#/pull_requests/2024/1
So yes, I think we're in the early days of Rust.
This is perfectly understandable if you view languages as tools. If the new tool can't do something the old one did, then you'll have questions about using it. A great example is inheritance - if inheritance is missing, that's a negative for me. I don't care about the philosophy, I just want to use the tool to produce programs better and faster. If it's missing features, that's a negative point.
Yeah. I think there’s also a weird way that newcomers get treated when they join a community. When you’re a newcomer to Rust, you probably have some preconceptions about how Rust should work based on the other languages you know, and you’re probably running into a lot of the same problems with Rust that everyone else has (e.g. borrow checker).
Most of the community is just kinda tired of the discussion and tired of answering the same questions, so they don’t interact with the noobs at all. That means that the people who, as you say, “thrive on dunking on noobs all day long”, are the primary point of contact for noobs.
Finding a decent programming community these days is a pain in the ass. The cool people, i.e., the people working on cool projects and getting shit done, are mostly busy and not hanging out with anybody.
>You just need to read another 50,000 word fasterthanlime essay. Then you'll not have problems any more.
I'm reminded of a line from Hachiman in Oregairu: "Something is only a problem when someone makes it a problem."
For some tasks, the tradeoff is worth it. Some individuals are naturally very inclined to use it.
But for most people, for most tasks, it's simply not the right tool. It's not a general purpose language by any stretch of the imagination.
I would argue, OS programming is definitely more general than game programming. Rust is great for OS related tasks, math, deep learning n other stuff, and maybe not so great at GUIs, or even bad at that, or just inconvenient as the article points out.
That means, it should be used for general stuff, and for programming games should be used a niche language, like Lua, Python etc. Especially for fast iteration and experimentation, break things and move lightning fast, typed languages are in disadvantage for sure, compared to untyped/weakly typed.
If C or Rust would not be available, you could still write your kernel in a well documented assembly dialect - some people still do - and then complete your OS userland in some garbage collected/interpreted/WASM abomination. But it's unlikely you could write Microsoft Word or a modern browser in assembly.
And this I think is true for games too, since, other than the performance requirement, they don't need the other features of Rust and are quite tolerant of failure. They also tend to be unwieldy beasts hard to design cleanly because the internal world simulation would become ever more complex, imposing weird data access patterns etc. If your online play code suddenly requires access to some internal "tank fuel" state, in C you would just pass the pointer and pray to the goods of rock and roll it's still valid at access time; a garbage collected language would at least give you that; but in Rust, you are looking at a complete refactoring of the entire "tank" module to make the new access pattern fit the ownership model.
As the post explains, this will kill quick iteration, which I think is a very general need of most programmers.
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.
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.
Rust -> move slowly and don't break anything
As someone who works in gamedev I can assure you C++ is same bad choice for 2 man indie project. In search of fast iteration times games have moved away from writing code in low level languages. Hardware for casual games is much bigger and faster than anything small team is able to make.
I'm pretty fast in Rust but not as fast as Go, mostly because Rust's type system and borrow semantics come with a higher cognitive load.
I find both to be faster than C++. Rust is faster because I have to worry less about blowing my feet off with memory errors. I can't think of anything to recommend C++ now that Rust exists.
Sure, but the problem is when you have a community around that tool that insists otherwise.
> From startups to large corporations, from embedded devices to scalable web services, Rust is a great fit.
Are these "systems"? FWIW "systems" does not appear on the Rust home page.
From wikipedia:
> Rust is a multi-paradigm, general-purpose programming language that emphasizes performance, type safety, and concurrency.
So... a systems programming language? x)
Rust is good and has earned its place. I just despise cult-like followings for these languages and technologies.
[1] https://www.youtube.com/watch?v=YZeZsZEEpno&themeRefresh=1
What is it called when a person or group's actions don't match their words?
Personally, I think downvoting insults is more inclusive than not downvoting insults.
> just like my earlier post
You made an entire submission asking users of a specific programming language to defend themselves.
It's a bad HN submission.
Flagging it is neither inclusive nor uninclusive. It's just marking it as a bad submission.
How do those actions fail to match words? What words do you have in mind?
Also the HN users that flagged it probably don't even use Rust.
For a moment I thought every hint of criticism towards Rust is suppressed or censored by the Rust zealots.
Just try finding e.g. an MQTT or WebSocket or etc library that doesn't drag the whole mammoth tokio ecosystem (which is really geared for Web Scale! projects) in with it.
Rust is becoming the language that tokio ate, and Cargo/Crates.io the new NPM.
That is, Rust is the systems language that a wave of non-systems developers insist on using, leaving behind a trail of non-systems-appropriate crates and projects.
Parent commenter's comment was crap by HN's commenting standards... but the underlying point about trendiness I think is in fact accurate.
(That said, your example might be a bit exaggerated as I found mqtt libraries that don't require tokio).
For myself that's looking more and more like:
Ditch crates.io (and maybe even Cargo), carefully curate and vendor all dependencies.
Probably avoid async, but definitely avoid tokio.
Don't get excessively clever. (Here I think Rust does a better job of C++ of having good "community standards" already)
(How is tokio fundamentally different than boost.asio and beast in this regard?)
> That is, Rust is the systems language that a wave of non-systems developers insist on using, leaving behind a trail of non-systems-appropriate crates and projects.
I have hard time understanding how this "trail" of stuff (evoking imagery of pollution) that no one is forced to depend on nor is promoted in any special way in the core library or language is any meaningful impediment to developing and distributing libraries more appropriate to the embedded domain, but perhaps one can explain how.
I used to read something similar all the time around 20 years ago: I don't have anything against Jesus. It's his fan club that's a pain in the ass.
Look at C++, which can both dick with move semantics but also offer multiple inheritance. Even better, look at C#, which has pointer fiddling but also offers the best reflection of any language today.
You're right that Rust is only really good for kernel development but it didn't have to be that way.
Hopefully, it's perfect for more than that.
Rewriting a system that's already acheived that scale is a contentious decision, to say the least. Many of our colleagues have been lost to and scarred by well-meaning attempts to rewrite those systems in the past, including many who were sure it was the right choice for "safety and security" themselves. For every conceptual safeguard new tooling might give you, you invite countless and sometimes catastrophic regressions in actual application logic in writing new implementations. It can work out, but no tool can expect to live only on that kind of work.
I'm sure Rust has much broader applicability than that. Or I hope so at least.
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.
Why choose Rust if you don’t care about maintainability and long term stability? Those are core values of the language!
The language choice was wrong from the start. C++ is king for games so if you care more about delivering features and fast prototyping why not go with that?
Maybe Rust is not a good language for rapid iteration in the game industry. And that’s ok I think.
Personally I like Javascript
It makes even less sense to use Rust to replace one of the higher level languages like C#.
I've run into this in a side project I'm working on, where my indecision over which ownership models are actually workable in the API to satisfy the needs I have means almost all of the coding is spent just proving I can get an ownership model working over having a skeleton code that can do something. And still, even as lacking as functionality as this codebase is, swapping to a new ownership model takes several minutes of coding. Trying to do this kind of exploration on a codebase that has real functionality would mean spending hours of change just to move some property from here to there.
I really enjoy having automated tests and automated tests solve problems. Like, I absolutely love our molecule test suite for our infrastructure code and it gives us huge value. The smoke tests we have for the infrastructure running after deployments are amazing. It's liberating in an environment of rapid and chaotic changes everywhere, complex interdependencies, as well as pressure on top.
However, if I try to transfer that kinda approach to a game it just... fails?
But the realization is: In games, many behaviors and systems are actually far simpler and less intertwined in arcane ways, and code changes actually less frequently and dramatically than at work.
I could see structured testing in e.g. turn based strategy games and such, so you can test that the culture per turn is correctly calculated based off of many different situations and such.
But in many smaller projects I've had, you write some janky code, make sure the enemy behaves as expected... and never touch or change that piece of code ever again. And it just doesn't break, because no fundamental part below it ever changes, because then the entire house of card would fall apart.
It feels dang weird, but it works very well for smaller projects.
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.
>> Their lack of care for perf
>> I have to explain yet again
If this is the kind of attitude I would get from using Rust and having to be part of the Rust community, then I think I will pass.
1) https://news.ycombinator.com/item?id=40175427
Implies OP doesn't care about programming:
> 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.
2) https://news.ycombinator.com/item?id=40173609
Handwaves Rust's complicated and onging async history and pinpoints the problem to "this guy":
> Why is async such a dealbreaker for this guy? Especially for web dev.
3) https://news.ycombinator.com/item?id=40172636
Another "you're holding it wrong":
> That has got to be the most "I didn't think this through" take ever.
4) https://news.ycombinator.com/item?id=40172605
As a newcomer, not thinking the Rust way is a sin and if you ask dumb questions you deserve retaliation, apparenlty:
> "On the other hand, Rust communities are inundated with people trying to write Rust as if it was their old favorite language..."
> "..In my experience, Rust community members who arrive with well thought out complaints or suggestions are welcomed by the people who like working on programming language fundamentals."
5) https://news.ycombinator.com/item?id=40172883
More handwaving of Rusts shortcomings. Plus contradiction on the same sentence:
> "Rust is not antithetical to iteration-based programming, it just makes you write a lot of heavy boilerplate to explicitly support that kind of style."
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.
https://unity.com/pricing-updates
NOTE: I'm not a game dev
NOTE: By "compose" I mean that if I want feature A, B and C from lang X (has A and B), Y (has B and C) and Z (has A and C), but there is no way to get A, B and C in one language without creating a brand new one. I cannot mix and match features from different languages.
(When are we getting a low latency collector in the CLR? But I digress...)
(me not knowing might be the ignorant bliss that allows me to just do productive things in them regardless)
Edit: I understand from the updated comment
I've long accepted that no one language can offer all of the language features I'd want, but I also question if I'd even want that language if I got it
Rust + (garbage collector)
Swift + (cross platform support)
Kotlin + (proper pattern matching)
Unfortunately, none quite hit that central sweet spot, IMO
Borgo looks neat - hadn't seen it before.
If you do, I'd ask you to give some thought to FFI efficiency. I've been wondering if there could be a good way in a transpiled language for the type system to validate at compile time that any pointers passed to C are pinned, so you can safely run the FFI calls with cgocheck=0 which eliminates quite a bit of overhead.
One could also use Go-Assembler as a cross-platform assembly target: https://go.dev/doc/asm
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!).
https://devpost.com/software/escape-from-heat-island
(Judging is still ongoing and votes would really be appreciated! It would help me to get more resources to work on the underlying package).
This was my first ever “game” (tech demo, really), and I’m not a game dev, so take this with a grain of salt - but I do think there’s a lot of potential for Flutter/Dart as a game framework. Hot reload makes iterating on game logic very fast, you obviously get the UI toolkit and cross-platform support straight out of the box, and the language itself is (relatively) concise, so it lends itself well to gameplay programming. When you need to get your hands dirty at a lower level, you just drop down to C++ (or whatever engine you can expose via FFI).
I think Google believe that Flutter can nab market share from Unity in casual 2D games (hence their official sponsored competition), but I think it has even more potential than that. In fact, I’ve seen at least two game companies (Supercell and another whose name I’ve forgotten) hiring for people to work on embedding the Flutter engine in various platform games.
[0] https://github.com/nmfisher/flutter_filament.git
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.
Of course "smaller" is significant here; it has less features and you might not like using it anymore.
It is clearly stated on Swift's documentation, they already hold a couple of talks at C++ conferences about code migration, and is one of the reasons why nowadays they mostly focus on LLVM contributions instead of clang.
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.
No, Ada/Spark is an example of a good engineers language. Go is a mediocre effort at best. Rob Pikes defence is that it was designed for junior Googlers who "aren't capable of understanding a brilliant language". Yes that's a real quote.
I ran into these issues all the time with Java, C++, and Python projects.
But it's just not the experience of running Go in production, which I've been doing for over 10 years now, across many projects with many devs.
In practice, nil checks are just not very difficult to include everywhere. And experienced Go programmers don't use exceptions (panic/recover) almost ever.
1) Anecdotal
2) Based on faith that someone will not forget to do something instead of a well documented mechanism in the language that could block that from the start
Having nil/null to handle empty references is simply very bad design and there's decades of examples why. The correct way is using a two-value type like Option, Maybe, etc. so that the (possibility) of the value missing is actually encoded in the type system
I can't see it in the feature list of JDK 22 or even 23. Maybe it'll come in JDK 24?
https://openjdk.org/jeps/401
I don't follow the JEP process closely enough to know if it will be proposed for 23 or approved. But I think it's coming soon.
They would get it sooner by breaking the ecosystem, and as Python 3, Java 9, .NET Core have shown, not everyone would be racing to adopt the new shiny thingy.
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.
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:
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...
I can recommend https://fsharpforfunandprofit.com/ as a starting point.
If there's interest, I can split some of my code into stand-alone chunks and post my experience of what worked well and what didn't.
I wanted to share some thoughts on here on what brought me to F#. Maybe this can serve as a starting point for people who have similar preferences and don't know much about F# yet.
A big part that affects my choice of programming language is its type system and how error handling and optionality (nulls) are implemented.
That "if it compiles, it runs" feeling, IMO, isn't unique to Rust, but is a result of a strong type system and how you think about programming. I have a similar feeling with F# and, in general, I am more satisfied with my work when things are more reliable. Avoiding errors via compile-time checks is great, but I also appreciate being able to exclude certain areas when diagnosing some issue.
"Thinking about programming and not the experience" the author lamented in the blog post appears to be the added cost of fitting your thoughts and code into a more intricate formal system. Whether that extra effort is worth it depends on the situation. I'm not a game developer, but I can relate to the artist/sound engineer (concept/idea vs technical implementation) dichotomy. F#'s type system isn't as strict and there are many escape hatches.
F# has nice type inference (HM) and you can write code without any type annotations if you like. The compiler automatically generalizes the code. I let the IDE generate type annotations on function signatures automatically and only write out type annotations for generics, flex types, and constraints.
I prefer having the compiler check that error paths are covered, instead of dealing with run-time exceptions.
I find try/catches often get added where failure in some downstream code had occurred during development. It's the unexpected exceptions in mostly working code that are discovered late in production.
This is why I liked Golang's design decisions around error handling - no exceptions for the error path; treat the error path as an equal branch with (error, success) tuples as return values.
Golang's PL-level implementation has usage issues that I could not get comfortable with, though:
Most of the time, I want the code to terminate on the first error, so this introduces a lot of unnecessary verbosity.The code gets sprinkled with early returns (like in C#):
I noticed that, in general, early returns and go-tos introduce logical jumps - "exceptions to the rule" when thinking about functions. Easy-to-grasp code often flows from input to output, like f(x) = 2*x.In the example above, "file" is declared even if you're on the error path. You could write code that accesses file.SomeProperty if there is an error and hit a null ref panic if you forgot an error check + early return.
This can be mitigated using static analysis, though. Haven't kept up with Go; not sure if some SA was baked into the compiler to deal with this.
I do like the approach of encoding errors and nullability using mutually exclusive Result/Either/Option types. This isn't unique to F#, but F# offers good support and is designed around non-nullability using Option types + pattern matching.
A possible solution to the above is well explained in what the author calls "railway oriented programming": belinder ↗ Thanks so much for your detailed reply. This looks very cool indeed. I've had a couple tiny projects in F# in the past that never went anywhere, but you're describing essentially all the parts in a programming language that I want, early returns, binds/maps, language support for these features, defining your own keywords (not really but kinda with your expressions)
Excited to try this out
Maybe I should just unsafe rust and see how I go....
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.
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.
Not having a solution is not the same as having an opinion.
If you have years to spend on plugging at ABI stabilisation, generics and proc macros in dynamic linking, and redistributable std, I’m sure the core devs would be happy for you to.
> Rust also really wants you do to use their build system and package manager, you can avoid both but everything will fight you.
What do you mean everything will fight you? It sounds like you’re confusing rust and its ecosystem.
So have your network protocol parser in Rust and the entire rest of your game in whatever the hell you want.
All the practical safety you could desire without any language constraints for the other 99% of the code
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.
This isn't even close to being true. I think memory safety isn't as important for games as it is for most software (though it is still quite important for multiplayer games!). But even if you write the most modern C++ possible I guarantee you are going to spend some of your time debugging segfaults, memory corruption and heisenbugs. Don't try and claim "I don't write bugs". Everyone does.
It is kind of difficult to have a segfault or memory corruption with explicitly paged object memory, since there can't be any pointers and these complex objects are bound-checked at compile-time. If you care about performance and scalability, you don't need to concern yourself with multi-threading as an issue either. The main way you'd expect to see memory corruption is if you try to read/write a page in the middle of a DMA operation to the same memory, and Rust doesn't help you with that either (though this would be just a normal logic bug in the scheduler).
It is pretty easy to avoid segfaults and memory corruption in modern C++ if the software architecture doesn't allow you to create the conditions under which those are likely to occur.
https://jira.mariadb.org/browse/MDEV-14248?jql=text%20~%20%2...
As far as I can tell about 5% of mariadb bugs mention segfaults, compared to 0.2% for SurrealDB.
I mean it's fairly obvious that even if some code in a Rust database is `unsafe` because it deals with manual paging and DMA and whatever, most of the code is going to be safe code.
I always found it theoretically interesting that schedule-based safety architectures, which are focused more on optimal resource allocation than safety per se (its all about extreme throughput traditionally), asymptotically converge on memory safety too as a practical matter for the same reason they also require almost no locking. By doing the safety analysis (many kinds, not just memory) at runtime, tiny dynamic modifications to the execution schedule are sufficient to provably (using TLA+ and similar) avoid many types of "unsafety" without the design compromises required to enable some of this analysis at compile-time. It requires a non-traditional software architecture, and it doesn't play nicely with a lot of existing code, due to the level of execution control required but I see more and more systems being designed this way at the high-end of the data infrastructure market.
Go without GC is more like a Go and Zig baby.
and having explicit allocator in standard library is actually a good thing, cause it's quite a common case in game development to use arena allocators which are being freed once per frame - so you don't really need to reinvent your data structures in Zig
I do have some concerns about Zig because it also introduces some friction for correctness sake like requiring to always use all variables, explicit casts everywhere - I want some compiler toggle to disable all of that and focus on problem but unfortunately it's not there
I am playing with Zig now and haven't really formed my opinion about game development specifically but I like it a lot so far
In practice, it's only really a problem if you're doing codegen.
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.
https://odin-lang.org/
Nitpicking: I'm not fond of reserving keywords like len or append.
Better is Also is quite verboseMaybe better would be
And it takes a lot of people to make good tooling.
They're mostly useless and a visual annoyance.
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
I've not experienced this. Do you have examples of the rust community flaming someone for having negative opinions about the language?
You were flagged for a pointless quip about "woke"ness. Other people repeated more civil and reasonably argued forms of your same point about the language and its community and received no such downvotes.
No need to play martyr.
Try criticizing Rust next time, if you want to demonstrate that criticizing Rust gets downvotes. You were throwing out insults in your very first comment.
Hopefully they calm down, or really get drown out, once there are a real number of jobs for people using Rust. Right now the evangelists outnumber the rank and file who are just using a language to get work done.
They've gotten way nicer.
Let's assume that the newsgroup is important. Legendary Lisp hacker Alan Bawden posted there just last week or so. Nobody ever mentions him.
In this case I think people should learn from history and that specific examples are the best way to do that. It's, like, effective pedagogy or whatever.
Supposedly Clojure people are nice though.
All too often you get ignorant remarks, jokes about parentheses or "contents of address register", or outright ridicule.
If you say anything frank at that point, chances are good you might come off as an asshole.
Just smile, nod your head, change the subject, and never return to it.
In fact, my experience has been the polar opposite, the rust community has been very friendly and accepting of critique.
So again, I'm going to ask for an example of rust language fanatics frothing at a criticism. If it's such a community problem this should be easy to find correct?
Here's the OPs article on /r/rust and it's both got a fair number of up votes and the top comments are all really positive towards this article. That's what I've seen at typical in the rust community.
https://www.reddit.com/r/rust/comments/1cdqdsi/lessons_learn...
I've seen this everywhere. This is an obnoxious, lazy thing to say to someone. It's a go to for many "enlightened" languages that have small ecosystems and something to prove. The only response is to ignore it entirely or, like the author did, dedicate years of your life just to see if there's something to it. This is not okay. Life is short, and we lean on other developers experience to keep us from wasting our time.
If someone posts a topic wondering if X language is bad for something, it's an earnest question. Not a time to flex your dedication to the cause.
It really depends on where and how the discussion is taking place, and what censorship methods the website/platform/medium involved offers.
Sometimes users are just outright banned or shadow-banned, if those happen to be options.
Sometimes forum threads, bug reports, or comments are deleted.
Sometimes the discussion remains accessible, but is stifled in some way. This includes closing/locking forum threads or bug reports, or otherwise severely limiting participation in such discussions to a very small and isolated group of people. If down-voting/reporting systems are present, sometimes they're used to limit the visibility or prominence of such discussion.
This bug report, for example, has various "This comment has been minimized.", "rust-lang deleted a comment from ...", "rust-lang locked and limited conversation to collaborators" interference:
https://github.com/rust-lang/team/pull/671
A Reddit thread discussing the situation from that bug report mentioned above has numerous "[removed]" comments and down-voted comments:
https://old.reddit.com/r/rust/comments/qzme1z/moderation_tea...
When Rust is discussed here, it's common enough for reasonable and relevant Rust-related comments to be voted down, sometimes severely. These threads have some examples I quickly found via a search of high-activity Rust submissions:
https://news.ycombinator.com/item?id=24334731&p=2
https://news.ycombinator.com/item?id=24343867
https://news.ycombinator.com/item?id=23802674
https://news.ycombinator.com/item?id=26812047&p=2
https://news.ycombinator.com/item?id=29488336
https://news.ycombinator.com/item?id=11340100
https://news.ycombinator.com/item?id=24337001
Here's an example of a recent submission on this site for an article very reasonably and thoroughly questioning Rust. It got some attention, and now it's currently marked as "[flagged]":
https://news.ycombinator.com/item?id=40091427
Keep in mind that strict "moderating" (ie, censoring) has been an integral part of the Rust community's identity for many years now via its Code of Conduct and Moderation Team -
https://www.rust-lang.org/policies/code-of-conduct
https://www.rust-lang.org/governance/teams/moderation
https://news.ycombinator.com/item?id=32117148
https://news.ycombinator.com/item?id=39641552
These threads are absolutely painful to read. The Rust community/leadership would not do anything about it because Rust thrives on such "devotion".
Not actually flaming but quite condescending towards the article writer. Not even properly reading the article and coming to conclusions.
This is on HN which is generally more neutral towards Rust. I imagine in Rust circles these types of responses would come out a lot more.
If you go into the comment section of a pro-Rust article, where the first few top-level comments are also pro-Rust, the responses to people expressing a negative attitude about Rust tend to (in my experience) be different.
This phenomenon certainly isn't exclusive to Rust (or HN). It happens all the time, especially when a prolific commenter is among the first few comments. It can set the tone for the entire comment section.
One way i've seen to reduce this is prefixing any posts with "I am not criticizing any engine in particular" even if it's blatantly obvious because the criticism only applies to one.
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...
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.)"
https://www.roc-lang.org/friendly
https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/defe...
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.
Sometimes, it's useful to bypass these things for a prototype and you don't care if it crashes on any edge case. This is why typescript is so popular on the web - you can quickly prototype something with JS, then once you find the right solution, add types and productionize the code
Most interesting programs involve a lot of figuring things out as you go, and so any "tax" on that process is one you hope to avoid. The last thing you want is for the language itself to be the source of that tax any more than it has to be.
[0] https://www.roc-lang.org/
> This is why typescript is so popular on the web - you can quickly prototype something with JS, then once you find the right solution, add types and productionize the code
I think you've got it backwards - TS is popular because people want to use types up front, but it has to work with JS, which is so dynamic that it's impossible to write a sound type system that can even touch it.
I don't want to reply "git gud" but I really am struggling to understand how people are writing code that where it is so difficult to use type information or where changing it is so cumbersome that you think it's a barrier. And I don't see many games or performant GUIs written in dynamically typed languages, particularly outside the web. Even in very dynamic languages like Objective C, things are still well typed.
I get it! But the compiler here is getting in the way, so Rust is the wrong tool choice here, or for anything that requires quick prototyping (like the OP said)
> writing code that where it is so difficult to use type information
It's not difficult, it just takes longer. Typing up front works well when you know exactly what you're building. When you don't, or are doing experimental design, they just get in the way until you've settled on a direction. This direction is not due to technical constraints or language choice, it's simply that designing complex user interactions is hard and you don't know it's correct until you have users use it
> dynamically typed languages
Because the cost of prototyping in a dynamic lang and then fully rewriting in a performant language is higher than having slower iteration speeds in typed languages. But also, this is why a large number of new non-web GUIs use electron (or are mac exclusive, which offers other benefits for GUI development)
> I think you've got it backwards - TS is popular because people want to use types up front,
Personally, I mix it up. There are things I know what their types will be no matter what so i add them up front. Then there are other things which I do not know, and I add those types at the end once i'm performing the final cleanup before code review
The nicest part is, TS runs regardless. I often refactor my types frequently since I get them wrong a lot (especially the up front ones) and have TS in a failing state while developing, but the TS compiler doesn't get in my way, and still works for the types that I expect to have working
```rust
struct Type1 { id: u32 }
struct Type2 { id: u32 }
fn main() {
}```
This code works perfectly fine. These two structs have the same signature. The only thing that doesn't work is the names. There are dozens of things like this where your code works but the compiler is too strict. This is terrible for rapid iteration. It's good for other things like modeling your domain with types.
> one is for things that are structurally identitical but semantically distinct where confusing them creates a semantic error, the other is for alternate (usually, more concise) names for an existing type
This is not always true. Sometimes there are cases where you have different type aliases and want to use them interchangeably. Now you have to refactor them to inherit from some general type that didn't exist before. I also could count on one hand the amount of times this has caught a genuine bug even with diligent domain modeling (Which I would argue is overhyped).
It can take longer to think about how to properly type instead of just writing some code, testing it out, and immediately seeing if something is wrong. You also often get into a situation where linters who like to act like type-systems give you arcane errors (looking at you TypeScript).
In the moment I just want to move some data around and quickly try out an idea, I don't need it to be resilient or perfect.
Because not every program that doesn't compile is necessarily invalid.
I think that more programmers should be aware of this point. Rust doesn't reject code that will crash, it rejects code that it cannot prove won't crash.
The code being rejected might be just fine (as numerous examples in the article showed).
The article addresses this, multiple times. In brief, the "poor structure" isn't the problem.
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.
Huh. Not games like Cyberpunk 2077 and it's good that they are not.
Because the type of game you produce in that time frame isn't typically the one that needs to worry about parallelizing multiple compute units.
Define gameplay. If you have some simulation happening as part of the gameplay, parallelizing it can be quite useful vs killing one CPU core on it. Physics is just one common example, but not everything is about physics. You can simulate whatever.
If one or two threads(game loop & rendering threads) is enough why increase the difficulty by going even more concurrent, for little to no extra payout.
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:
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:
making it easy to switch between the two, and also switching function parameters from values back and forth with references.Multiply that by all the C(++) coders on the planet and we have lost a billion man hours...
https://github.com/tsoding/tic-tac-toe-auto-deref
https://www.youtube.com/watch?v=yKI-VOBBFu8
On the opposite other languages want to have no hidden function call, no hidden pointer dereferencing..
Which will never happen
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.
Ironically, the main thing holding Bevy back is the bickering at the W3C. WebGPU is still not widely supported, so we have to support WebGL 2 (with reduced functionality in some cases), and that adds a lot of complexity.
I think the article is precisely criticizing this type of comment... You make people believe that Bevy is some kind of safe bet for the future, that it took inspiration from the greatest to build even better foundations for a game engine... And it seems common in the Rust community: make audacious unverifiable claims to enroll other "believers". But it's easy to claim when the tool itself has a fraction of the functionalities of Unreal, Unity or Even Godot. Heck, last time I used it (about 2 years ago) there wasn't even any built-in physics stuff. You have to install plugins from every corner, some require old Bevy versions, some other require newer versions... It's seriously unusable to just "get things done"
- One where most things are done out of the box, and you can mostly GUI visual code to success - these are your Unreals, your Unitys and Godots.
- Second, where most things need to be built piecemeal, and many things are missing, and need to be built - Bevy falls into this category, along with stuff like PyGame and what not.
I mean, Bevy is a fine engine for small things, but I can't admit I've seen any indie game of more renown succeed with it. OTOH, doesn't mean you can't make it, but it's definitely more effort.
As for audacious unverifiable claims: it's quite verifiable that Bevy never used DirectX 9, DirectX 10, shadow volumes, HLSL, Cg, Vulkan, or Metal (I'm not sure about OpenGL or GLSL; those were before I got involved with the project). It chose the better path (wgpu) early on. That's all I was saying.
Well, the example code for a simple button referenced in the article has insane complexity that has nothing to do with WebGL though. It's precisely "the right way" that adds a lot of complexity.
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.
Seems promising, very React-esque with little boilerplate
Why should anyone choose Dioxus over Sveltekit, Next or Nuxt? I never had an issue with a frontend app that the borrow checker would have catched. Error handling was an issues some years ago but is solved by now when using one of those modern frameworks. (I don't know if Dioxus has error boundaries, though.)
Those Rust fullstack frameworks make sense only for people wanting to use Rust, not for people looking for the right tool for the job.
However, I am also using Rider.
* name intentionally made to make people angry
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.
is not something that exists in the real world.
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.
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.
Is it? The article addresses that, and basically calls it a pointless feature that is almost never used and when it is the benefits are mostly lost because of real world needs and constraints, and that the problems it solves are easier solved through other solutions and add-on systems that are well understood.
I think this might be a case where explaining the real-world benefit instead of the theoretical benefit is needed, if only to counter what are very pointed criticisms that are definitely deeper than at the theoretical level.
You can see that all the CPUs are being maxed out. This actually does result in significant FPS increases. Does it matter for every game? No. But it does result in better performance!
>> Is it? The article addresses that, and basically calls it a pointless feature
> You can see that all the CPUs are being maxed out.
You're missing the forest for the trees - the poster above basically said "seeing all the CPUs being maxed out is a pointless feature" and you reply with "but see, all the CPUs are being maxed out".
You're literally ignoring the complaint and replying with marketing.
> Unfortunately, after all the work that one has to put into ordering their systems it's not like there is going to be much left to parallelize. And in practice, what little one might gain from this will amount to parallelizing a purely data driven system that could've been done trivially with data parallelism using rayon.
It's not saying "yes, you get parallelism, but I don't need the performance"; it's claiming that in practice you don't get (system-level) parallelism at all. That's at odds with my experience.
So, while yes it's nice in theory, in practice it often doesn't add as much performance as you'd expect.
> the impredictability of parallelism (both in ordering and in timing) forces the developer to add [...] dependency constraints
The fact that it is possible to make a benchmark without hitting this problem does nothing to prove that bigger games can avoid it too.
Hitting 100% CPU means nothing unless those cores are doing things you actually want them to do.
The way I interpret the claims is that bevy is putting far too much focus on performance and multi-threading when by far the important thing to focus on for game development is allowing the actual game developers to rapidly iterate.
Bevy might be very fast and performant, but if that seems to have come at the cost of (or been optimized for over) features that make it easier to use in ways that game developers need, then the criticism may have merit. Whether that's true or not I don't know, but hopefully that explains why a response about how it definitely can use lots of threads and make good use of many cores isn't really seen as a good rebuttal to the criticisms leveled.
I'm fully in favor of having Bevy support dynamic languages, as implemented in for example bevy_mod_scripting [1], for projects that don't need that parallel performance.
[1]: https://github.com/makspll/bevy_mod_scripting
We've been promised automatic CPU scaling in programming languages since at least 2001, and I've yet to see any practical version of it.
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.
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?
Memory safety and memory management are different things. Scripting languages remove the burden of manual memory management; as a side effect, they also tend to be memory safe, but that hasn't been the main motivation.
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.
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.
As a counterexample, .NET suffers a lot from the lack of evangelism - big chunk of community that started out back in .net framework days still thinks of it as poorly as people outside the ecosystem because they never bothered to drop old and obsolete tools and targets and give new versions a proper try (as the code is often vastly simplified and performance is vastly better).
Other programming languages, not only Rust, also do better at self promotion - take for example Go that managed to convince everyone to put it in the same bucket as Rust (which, personally, I find absolutely insulting as C# is a much closer alternative to Rust both in performance, features and access to low-level bits).
Unless we already do...
It is, of course, difficult to convey to developers who only experienced C and C++ build systems, or Ruby tooling and brittleness, or Python way of managing dependencies, or setting up the packaging when using Java, that fast and easy to use solutions do not come from trade-offs but from just better ways of doing so - using cargo and Rust or dotnet and C# is night and day difference compared to options listed above.
I said it here in the past and will say it again: it's not that Rust (or .NET for that matter) are that good, it's a lot of other popular languages and platforms are that bad at one or another aspect (or many at the same time), that make it sufficiently painful to never tolerate a downgrade when you worked with a tool that offers better all-around experience.
Rust is deemed to have good tooling, but the third-party library ecosystem is following the NPM/RubyGems culture with all the fragmented dependencies, plus the added complexity of compile times due to lack of ABI compatibility.
Meanwhile, monolithic projects like Tokio also keep strengthening their reign among the small peasant crates.
I'm learning Rust, after decades of various languages with garbage collector, and I believe in the language itself and its tooling. But everything else about Rust irks me.
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)
Not to mention smaller projects like 'Braid- anniversary edition'.
Even in hindsight it's hard to judge whether building your own engine was good or bad decision, and we are nowhere near "the hindsight" level of knowledge.
A machinist who retires from running lathes and goes into the lathe-making business has reached the pinnacle of that profession.
I wish. As far as I can tell he made a single hour long video shitting on rust and now he's the enemy of the cult. That's hardly spending the last decade sitting being mad over rust.
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.
> The moment you allow this, you have to find a way to pick between several implementation - and they don't always have sane names.
There are other possible solutions that don't involve that.
For instance, many applications would be quite happy with "There can be only one implementation", giving a compiler error if there's more than one.
A slightly more sophisticated rule would be "Identical implementations are allowed and treated as a single implementation". This would be really convenient in combination with some kind of "standalone deriving" mechanism, which would generate identical implementations wherever it was used.
And if the original poster had said that I would be ok. Instead what they said is:
> It's a great example of something I'd call "muh safety", desire for perfection and complete avoidance of all problems at all costs, even if it means significantly worse developer ergonomics.
This implies the writer didn't assume what happens if you "turn-off" orphan rules. I.e. you don't trade perfection for developer ergonomics, you trade one set of developer (ability to write any trait for any type) ergonomics for another (having to battle two incompatible trait implementations from crates you don't own).
Either you have to manually specify how nearly every implementation is pulled (horrible developer ergonomics) or, even worse, you go into monkey patching territory.
> For instance, many applications would be quite happy with "There can be only one implementation", giving a compiler error if there's more than one.
Ok. But you still need a resolution mechanism to fix the error. Which implies manually solving ambiguity. And how do you solve it for blanket implementations?
At least initially, the resolution mechanism could be "don't include more than one implementation".
To reuse the canonic example:
Say by transitive dependencies, alpha_v0 and beta_v0 are imported, and due to vulnerability, you upgrade beta_v0 -> beta_v1 (let's assume that trait is essential for the vulnerability resolution).Now what? You either have to skip future updates, or keep the vulnerable beta_v0 crate.
Ideally, people would tend to put trait implementations for a given pair of crates in a unique library crate, implement them in the obvious way, and provide that as a library for people to use.
That's not a downside, that's dependency hell with Trait coherency landmines.
It implies that the writer would prefer using a different language, not that Rust would be better if it was all the same but with the parts he doesn't like taken out
That's not what they wrote.
> There are mostly valid reasons for wanting the orphan rule for things such as libraries uploaded to crates.io, and I am willing to concede that crates published there should obey this.
> But I have a very hard time caring about this rule for applications and libraries developed in end products. I'm explicitly not saying binary crates, because most bigger projects will be composed of more than one crate, and many will be more than one workspace.
It talks explicitly about Rust. In hindsight, the author might have been better suited by another language, but expecting Rust to suddenly become another language is weird.
- Once you get good at Rust all of these problems will go away - Rust being great at big refactorings solves a largely self-inflicted issues with the borrow checker - Indirection only solves some problems, and always at the cost of dev ergonomics - ECS solves the wrong kind problem - Generalized systems don't lead to fun gameplay - Making a fun & interesting games is about rapid prototyping and iteration, Rust's values are everything but that - Procedural macros are not even "we have reflection at home" - ...
the list corresponds to the titles of sections in the article.