Due to potential new direction of D, I’m looking for some escape route just in case. I’m primarily a gamedev, so no functional programming languages like Rust or Haskell. Also one of the features I dislike the most in C/C++ is the super slow and super obsolete precompiler with its header files, so no zig, I don’t want to open two files for editing the same class/struct. Memory safety is nice to have, but not a requirement, at worst case scenario I’ll just create struct SafeArray<Type>. Also I need optional OOP features instead of reinventing OOP with all kinds of hacks many do when I would need it.

Yes I know about OpenD, and could be a candidate for such things, just looking into alternatives.

  • Ephera@lemmy.ml
    link
    fedilink
    arrow-up
    16
    ·
    14 days ago

    The cool kids in systems programming are using Rust now. I have no idea if game devs are using it.

    It’s not fully there yet for production use, but there is certainly interest:

    • There’s Rust bindings for scripting Godot.
    • A game engine is being built in pure Rust, called Bevy. It’s still lacking e.g. a GUI editor, but people are already building smaller games with it.
    • And there’s a somewhat more established gamedev studio, Embark Studios, which is contributing quite a bit to the Rust gamedev ecosystem. Might know them from the title ‘The Finals’, which however wasn’t yet implemented in Rust.

    One thing to note about Rust for gamedev is that it enforces correctness to a degree which makes it cumbersome for prototyping.
    Using it with an ECS already alleviates a lot of that pain (by sidestepping Rust’s memory management). And I do imagine, much like in C++, that more and more abstractions will be built on top of it to eventually make it very nice to use.
    But yeah, Rust isn’t as clear-cut of an upgrade yet, as it is in some other fields.

      • Ephera@lemmy.ml
        link
        fedilink
        arrow-up
        8
        ·
        edit-2
        14 days ago

        It’s the “Entity-Component-System architecture”, consisting out of:

        • entities, which are basically just IDs to associate different components,
        • components, which are individual data points like the health or the position or the velocity of an entity, and
        • systems, which operate on components to introduce interactivity. For example, you might have a system which looks at all entities with a position and a velocity component, and then it just adds the velocity to the position. Then another system checks for collisions based on the position component and the dimensions component, and then subtracts from the health component when a collision happened.

        It’s kind of a competing strategy to OOP. It offers better performance and better flexibility, at the cost of being somewhat more abstract and maybe not quite as intuitive. But yeah, those former two advantages make it very popular for simulations / gamedev. Any major game engine has an ECS architecture under the hood, or at least something similar to it.

    • calcopiritus@lemmy.world
      link
      fedilink
      arrow-up
      3
      ·
      14 days ago

      Using “clever” ways to disable the borrow checker is one of the few things I don’t like about rust. I much rather it having a “borrow checker version” and a “garbage collector version”. That way we could rapidly iterate through design choices with the GC, and once the design has proven good, apply lifetimes and such to use with the borrow checker. The only downside to this I can think of is that most would just leave it in the GC version and not bother to move to borrow checker. But that’s fine by me, rust has many other features to take advantage of. As long as no GC libraries are allowed in crates.io, it should be fine.

      • Ephera@lemmy.ml
        link
        fedilink
        arrow-up
        3
        ·
        14 days ago

        Yeah, I don’t think that can happen without splitting the whole ecosystem in half. Garbage collection requires a runtime, and tons of the code semantics are also just different between the two, particularly with asynchronous code.

        I also imagine that many people wouldn’t just leave their particular program in the GC version, but never even bother to learn the ownership/borrowing semantics, even though those largely stop being painful after a few months.

        But yeah, I actually don’t think it’s too bad to have individual parts of the ecosystem using their own memory management strategies.
        The two examples that I can think of are ECS for gamedev and signals/reactivity for UI frameworks. Which is what is used in C++ or JavaScript, Kotlin, too. You’d probably still want to use these strategies, even if you’ve got garbage collection available…

        • calcopiritus@lemmy.world
          link
          fedilink
          arrow-up
          2
          ·
          edit-2
          13 days ago

          I’ve re-thought about the problem and I think for prototyping I should just Box::leak() and work with raw pointers.

          This approach also doesn’t allow you to not learn the borrow checker, since leaking everything is not a good memory management strategy, but might be fine for rapidly iterating on a design.

          EDIT: maybe use a leak!() Macro that does Box::leak(Box::new()) in debug mode but panics on release (as long as you execute once in release it should be fine).

          • Ephera@lemmy.ml
            link
            fedilink
            arrow-up
            2
            ·
            13 days ago

            I tried something like that once. Basically, I was trying to create an API with which sysadmins could script deployments. That involves lots of strings, so I was hoping I could avoid the String vs. &str split by making everything &'static str.

            And yeah, the problem is that this only really works within one function. If you need to pass a parameter into a function, that function either accepts a &'static reference which makes it impossible to call this function with an owned type or non-static reference, or you make it accept any reference, but then everything below that function has normal borrowing semantics again.

            I guess, with the former you could Box::leak() to pass an owned type or non-static reference, with the downside of all your APIs being weird.
            Or maybe your prototyping just happens at the top and you’re fine with making individual functions accept non-static references. I guess, you’ll still have to try it.

            Since you’re already at the bargaining stage of grief programming, maybe you’re aware, but Rc and Arc are the closest you can get to a GC-like feel. These do reference counting, so unlike GC, they can’t easily deal with cyclic references, but aside from that, same thing.
            Unfortunately, they do still have the same problem with passing them as parameters…

            • calcopiritus@lemmy.world
              link
              fedilink
              arrow-up
              2
              ·
              13 days ago

              The good thing about Box::leak() is that it returns a raw *mut pointer. So you need unsafe{} to dereference it. Might as well: let my_ref = &mut unsafe{*ptr}; while you are at it, so you have a perfectly normal rust reference, so the function signatures don’t need any change.

              The problem with Rc is that it would also require a RefCell most of the time. So the whole thing would be filled with Rc<RefCell<T>>. With the required .borrow_mut(). It would both do a pain to do and undo.

              And of course I want to undo it, because RC is a shitty GC.