Comment by lynndotpy

4 months ago

I love Rust, but this lines up with my experience roughly. Especially the rapid iteration. Tried things out with Bevy, but I went back to Godot.

There are so many QoL things which would make Rust better for gamedev without revamping the language. Just a mode to automatically coerce between numeric types would make Rust so much more ergonomic for gamedev. But that's a really hard sell (and might be harder to implement than I imagine.)

I wish more languages would lean into having a really permissive compiler that emits a lot of warnings. I have CI so I'm never going to actually merge anything that makes warnings. But when testing, just let me do whatever I want!

GHC has an -fdefer-type-errors option that lets you compile and run this code:

    a :: Int
    a = 'a'
    main = print "b"

Which obviously doesn't typecheck since 'a' is not an Int, but will run just fine since the value of `a` is not observed by this program. (If it were observed, -fdefer-type-errors guarantees that you get a runtime panic when it happens.) This basically gives you the no-types Python experience when iterating, then you clean it all up when you're done.

This would be even better in cases where it can be automatically fixed. Just like how `cargo clippy --fix` will automatically fix lint errors whenever it can, there's no reason it couldn't also add explicit coercions of numeric types for you.

  • > I wish more languages would lean into having a really permissive compiler that emits a lot of warnings. I have CI so I'm never going to actually merge anything that makes warnings. But when testing, just let me do whatever I want!

    I’d go even further and say I wish my whole development stack had a switch I can use to say “I’m not done iterating on this idea yet, cool it with the warnings.”

    Unused imports, I’m looking at you… stop bitching that I’m not using this import line simply because I commented out the line that uses it in order to test something.

    Stop complaining about dead code just because I haven’t finished wiring it up yet, I just want to unit test it before I go that far.

    Stop complaining about unreachable code because I put a quick early return line in this function so that I could mock it to chase down this other bug. I’ll get around to fixing it later, I’m trying to think!

    In rust I can go to lib.rs somewhere and #![allow(unused_imports,dead_code,etc)] and then remember to drop it by the time I get the branch ready for review, but that’s more cumbersome than it ought to be. My whole IDE/build/other tooling should have a universal understanding of “this is a work in progress please let me express my thoughts with minimal obstructions” mode.

    • On the other hand, I can't count how many times I've written some code like

          let iteration_1_id = 10;
          dostuff(iteration_1_id);
          let iteration_2_id = 11; // warning, unused variable!!
          dostuff(iteration_1_id);
      

      and then spent 10 minutes debugging why iteration_2 wasn't working, when it would have been resolved instantly if I had paid attention to the warnings.

  • Yeah this is my absolute dream language. Something that lets you prototype as easily as Python but then compile as efficiently and safely as Rust. I thought Rust might actually fit the bill here and it is quite good but it's still far from easy to prototype in - lots of sharp edges with say modifying arrays while iterating, complex types, concurrency. Maybe Rust can be something like this with enough unsafe but I haven't tried. I've also been meaning to try more Typescript for this kind of thing.

    • You should give Julia a shot. That’s basically that. You can start with super dynamic code in a REPL and gradually hammer it into stricter and hyper efficient code. It doesn’t have a borrow checker, but it’s expressive enough that you can write something similar as a package (see BorrowChecker.jl).

      2 replies →

    • Some Common Lisp implementations like SBCL have supported this style of development for many years. Everything is dynamically typed by default but as you specify more and more types the compiler uses them to make the generated code more efficient.

      1 reply →

Yeh, I've been tinkering around a year with a Bevy-competitor, Amethyst until that project shut down. By now, I just don't think Rust is good for client-side or desktop game development.

In my book, Rust is good at moving runtime-risk to compile-time pain and effort. For the space of C-Code running nuclear reactors, robots and missiles, that's a good tradeoff.

For the space of making an enemy move the other direction of the player in 80% of the cases, except for that story choice, and also inverted and spawning impossible enemies a dozen times if you killed that cute enemy over yonder, and.... and the worst case is a crash of a game and a revert to a save at level start.... less so.

And these are very regular requirements in a game, tbh.

And a lot of _very_silly_physics_exploits_ are safely typed float interactions going entirely nuts, btw. Type safety doesn't help there.

> Just a mode to automatically coerce between numeric types would make Rust so much more ergonomic for gamedev.

C# is stricter about float vs. double for literals than Rust is, and the default in C# (double) is the opposite of the one you want for gamedev. That hasn't stopped Unity from gaining enormous market share. I don't think this is remotely near the top issue.

  • I have written a lot of C# and I would very much not want to use it for gamedev either. I can only speak for my own personal preference.

I used to hate the language but statically typed GDscript feels like the perfect weight for indie development

  • It is indeed great for creating a prototype. After that, one can gradually migrate to Rust go benefit from faster execution times. The Rust bindings are in a pretty decent shape by now

    https://godot-rust.github.io/

    • Nowadays we have the luxury of LLMs to help migrate projects/code from one language to another. I would imagine a pipeline with Rust as an intermediate “compiled” step might be possible. LLM accuracy isn’t there yet, but I can dream.

      1 reply →

  • Yeah I haven't really used it much but from what I've seen it's kind of what Python should have been. Looks way better than Lua too.

    • I like it better than python now, but it's still got some quirks. The lack of structs and typed callables are the biggest holes right now imo but you can work around those

What numeric types typically need conversions?

  • The fact you need a usize specifically to index an array (and most collections) is pretty annoying.

    • Thats a feature not an annoyance. We need to keep Rust like it is to preserve its core value delivey: Robust software quality in exchange for development & compile time/pain, in other words: "the pain is moved from production to development" you can not have joy in both at the same time.

      Not sure if Rust should be promoted to build games, i prefer it being pushed to build mission critical software.

    • This could be different in game dev, but in the last years of writing rust (outside of learning the language) I very rarely need to index any collection.

      There is a very certain way rust is supposed to be used, which is a negative on it's own, but it will lead to a fulfilling and productive programming experience. (My opinion) If you need to regularly index something, then you're using the language wrong.

      27 replies →

  • What I mean is, I want to be able to use i32/i64/u32/u64/f32/f64s interchangeably, including (and especially!) in libraries I don't own.

    I'm usually working with positive values, and almost always with values within the range of integers f32 can safely represent (+- 16777216.0).

    I want to be able to write `draw(x, y)` instead of `draw(x as u32, y as u32)`. I want to write "3" instead of "3.0". I want to stop writing "as".

    It sounds silly, but it's enough to kill that gamedev flow loop. I'd love if the Rust compiler could (optionally) do that work for me.