Hacker News new | ask | show | jobs
by skohan 1376 days ago
Arguably Zig is a better fit for game dev. At the high end game dev is all about memory layouts and cache optimization, and that goal can run counter to Rust's approach to memory safety.
1 comments

Memory safety isn't in conflict with low-level control over memory layout.
It can just be limiting in terms of ergonomics. Like I've implemented a couple of ECS's and what you want to do is dispatch a bunch of threads over big swaths of structured memory. One of the main things the borrow checker doesn't want you to do is use the same memory on multiple threads. You can do that in rust, and people have, but you have to put a lot of effort into convincing the borrow checker that what you're doing is ok, or else find ways to side-step the borrow checker.

So basically rust imposes a lot of limitations on how you can structure your program relative to something like Zig or C.

> So basically rust imposes a lot of limitations on how you can structure your program relative to something like Zig or C.

I don't see it imposing that much of a limit. If there is code you know is correct and Rust can't reason about it, do it in unsafe block.

And ECS is not just possible in Rust, but due to mutability guarantees you can make your ECS schedule systems in such way to maximize multi threading. See Bevy ECS.

Using unsafe blocks would fall into the category of "sidestepping the borrow checker". Again, it's possible to do it, but if you're going to do everything in unsafe rust why not just use an unsafe language in the first place?

And how does rust allow you to "maximize multi-threading" over what's possible in other languages? Anything you can express in rust can be expressed in C or Zig as well. The only difference is the implementation you reach will come with static guarantees about memory ownership (so long as you don't use unsafe). So the set of all rust programs is a essentially a subset of all C/Zig programs.

> Using unsafe blocks would fall into the category of "sidestepping the borrow checker".

I kinda want to end this assumption. Unsafe only allows few things.

- access to raw pointers.

- implement or accessing unsafe functions (e.g. C FFI)

- implementing or accessing unsafe trait

- access to unsafe union

- mutate global variables.

You can't bypass borrow checker, and you wouldn't do everything in unsafe. If you write everything in unsafe you are definitely doing stuff very, very wrong.

Rust unsafe is a surgeon's equivalent of a scar. Some scars need to happen, and you can tuck them out of sight if you are clever. However if after an operation you look like a map of scars, it means your surgeon is lacking.

No language can prevent you from abusing it, but in Rust it's apparent when you do it.

> And how does rust allow you to "maximize multi-threading" over what's possible in other languages?

Bevy newbie here so take it with a grain of salt. Because Bevy knows when and how Resources are related it systems can freely run in parallel if two resources are unrelated and mutable, or related and immutable.

> Anything you can express in rust can be expressed in C or Zig as well.

Point of a good programming languagues isn't to enable writting programs that can do anything but to aid programmers in writing error-free programs.

Why do you think we use punctuation and spaces? I remember a talk[1] where he noted spaces/punctuation became a thing only when Christian monks started transcribing written words, as a way of error correction. This was how written word looked before:

Withoutspacesitsveryeasytomakierrorsandnotnoticeitinfactdidyounoticeitwhenimadeabunchrightnowassumingthispartdidntcauseheadacheforyouineithercasewrongcodeinrustlooksjustaswrongasthisparagraph

[1] EDIT: Found the talk https://www.youtube.com/watch?v=_EANG8ZZbRs&t=1418s

Thank you for the thoughtful response!

A few thoughts:

> You can't bypass borrow checker, and you wouldn't do everything in unsafe.

Isn't unsafe still bypassing the borrow checker, in the sense that you're mostly using it because you're doing something the borrow checker does not otherwise allow?

> Because Bevy knows when and how Resources are related it systems can freely run in parallel if two resources are unrelated and mutable, or related and immutable.

But again, I don't understand how that is a unique benefit of Rust. It's completely possible to implement something in C which allows multiple threads to run over unrelated memory locations, or the same immutable memory location. As a programmer you have more options for how to achieve this when not restricted by the borrow checker. Most of the ECS systems in existence solve exactly this problem, and probably most of them are implemented in C++.

> Point of a good programming languagues isn't to enable writting programs that can do anything but to aid programmers in writing error-free programs.

I mean, I think it can't be taken for granted that preventing errors should be the top priority of every programming language. It's probably a priority of most languages, but there's no reason it has to take the number one spot.

I would argue the universal priority of programming languages is to enable the programmer to deliver user value through their program. Rust's thesis is that memory safety issues are such an obstacle to delivering user value that the language should prioritize safety at the expense of a certain amount of pain for the programmer. Languages like Zig and Jai prioritize programmer velocity and relegate safety to optional static analysis tools.

Python is another example: it's very easy to write an incorrect Python program, but the barrier to entry is so low that it enables non-programmers like scientists to do very interesting things by copying a few lines off the internet into a notebook.

There's no one best set of tradeoffs. But as I said from the beginning, if one of the main tasks you are doing is manual memory management, as domains like game dev and HPC require a lot of, you might want to choose a language which prioritizes explicit memory management.