69 comments

[ 2.7 ms ] story [ 95.8 ms ] thread
Shouldn't it be "gamedevver" instead?

Anyway, on my personal wishlist: Allow customized pointers. For example, if I use a mmapped file that is mapped at a different base address every time it is opened, the pointers within that file are always relative to the base address. I'd like to use "special" pointers inside the mmapped file, that are aware of this. Also, I'd like to use standard data structures (associative arrays, lists, sets) inside the mmapped file, meaning that the solution should be generic.

That's a rather difficult sort of generic type to implement: it would have to include the base address in the type somehow.
Construct ptrs with the base address? At that point, why not just use '+' and be done with it.
> Anyway, on my personal wishlist: Allow customized pointers.

It's already there isn't it? Isn't a custom pointer just a stack struct implementing Deref (and possibly DerefMut)?

Can't you use raw pointers and .offset()? You can put them in a struct that mutates itself for every method call so it keeps track of the mmap. You'd also probably have to make sure that it has exclusive access to the mmap so another thread won't invalidate its pointers.
I think this can be implemented on current Rust, although you will have to define specific array, list and sets for this situation.

However, you would probably want a different implementation anyway due to the different performance characteristics of disk/SSD vs RAM.

When Jonathan Blow started his programming language specifically for making performant games (https://www.youtube.com/playlist?list=PLmV5I2fxaiCKfxMBrNsU1...), he talked about Rust a bit - it seemed like this was the closest language to what he wanted. I wonder if addressing these issues would make it more appealing to coders like him.
My impression is that the issue he had with Rust is that it enforces memory safety. That isn't something that is likely to change (although I would be open to it if there were a lot of demand—yes, really!) The Rust community, me among them, seems pretty happy with enforced safety.
I believe that Blow values ease-of-prototyping more than safety, and sees all of Rust's safety guarantees as a distraction. Games have smaller maintenance windows than other applications, so caring about security is definitely a harder sell (especially if the game is non-networked, so the attack surface is already minimized).

That said, the existence of Rust's thriving gamedev community means that not everyone shares this point of view, but I think there's still plenty of demand for a more specialized language for this niche.

> I believe that Blow values ease-of-prototyping more than safety

That's not entirely accurate. Based on his talks, he likes frictionless development over jumping through hoops that may or may not result in a safer application. Considering how many lines of code game programmers pump out on a daily basis, even small pain points can cause huge delays over a 2+ year dev cycle. Lines of code per day is a larger concern than safety for most games.

> Based on his talks, he likes frictionless development over jumping through hoops that may or may not result in a safer application.

I think it's clear that using Rust will result in a safer application per unit of developer time compared to a non-memory-safe language. But I'm certainly willing to believe that not everyone cares about having a memory-safe application.

It's not clear to me. Give me some proof and I'll change my mind. A scientific study, a case study, a statistical analysis, anything other than theory.

Haskell's strict typing system and theoretically correctness was supposed to lead to better code as well. I used xmonad for a few years. It crashes just as much as any other window manager.

The number of memory safety problems in memory-safe languages is much, much lower than in non-memory safe ones. Look at the classes of vulnerabilities in, say, Ruby programs and compare them to the number of memory safety-related security vulnerabilities in, for example, Firefox. I have actually analyzed this and memory-safety-related problems in Firefox are the majority of critical security vulnerabilities.
The number of memory safety problems in memory-safe languages is much, much lower than in non-memory safe ones

Citation?

I have actually analyzed this and memory-safety-related problems in Firefox are the majority of critical security vulnerabilities.

Cool. That means you can provide actual numbers and statistics... right?

Doubtful, he has his own programming language explicitly because of Rust forcing the user to do it 'the rust way' with boxed types etc. he doesn't want to be pigeonholed. It's not going to appeal to people like him, and I think the OP is getting into some good reasons on why Rust probably wont ever be a good language for game programming.
> I think the OP is getting into some good reasons on why Rust probably wont ever be a good language for game programming

This is much stronger than the OP was claiming, or than I would claim. None of the issues raised are fundamental; note that very few people in the Rust community, gamedev or otherwise, are asking for less safety. I write things very similar to games on a daily basis in Rust, and I've found that the Rust way actually works very well.

Nothing in that list makes me think Rust won't ever be a good language for game programming. It reads more like these are the current pain points that need to be addressed, and even with that list there are no real showstoppers there. Most are just nuances that they would like to see addressed.
It's a wishlist, and the OP is a prominent member of the Rust community. Most of those things are minor pain points (he even mentions "None of these are critical.") that he hopes can be improved on.

  > forcing the user to do it 'the rust way' with boxed types
This is mistaken, everything in Rust is unboxed and on the stack by default. You have to go out of your way to box something (with, appropriately, `Box::new` (or the forthcoming `box` operator)).
Is rust as complicated as it seems to me? I know ML derived languages well so I'm not a stranger to functional programming. And I'm a pretty competent C programmer, but it seems like Rust is about as complicated as C++, which I find offputting.
It depends on how you define "complicated." For example, Rust and C++ both have "move semantics," but Rust's is "A move is a memcpy, which the optimizer may or may not elide" and C++'s is http://stackoverflow.com/a/3109981/24817 rvalue references and copy/move constructors and all sorts of other things.

There are three major kinds of backgrounds that Rust programmers come from: functional, systems, scripting.

The functional crowd instantly groks Rust's pattern matching, first-class functions, and expression-based-ness. But they miss some more complex, stronger type system features.

The systems crowd instantly groks Rust's low-level features, but struggles sometimes with the compiler being so strict.

The scripting crowd instantly groks our tooling, and the functional-ish things, but struggles with the low-level.

So really, everyone has a different definition of what "complex" is, because it's based on what you're familiar with. Some people find map to be more complex than for, some people think the exact opposite.

All generalizations are false.

Heh, the three kinds of backgrounds sounds like the perfect description of what I've found in the Rust community (being of the functional variety myself). However, once I understood ownership and lifetimes, I felt like the whole world opened up. I'm still waiting for more ecosystem and community before I start to use it more seriously, but I'm chomping at the bit for that to happen.
> All generalizations are false.

I believe this sentence is a paradox.

It's not a paradox. It's just false. Compare "all sentences are lies".

It's close enough to true to be useful, but it's false when you treat the word "all" literally.

No, it's definitely nowhere near C++-complicated: there's usually one way to do things, no legacy cruft. There's no multiple inheritance nor large type hierarchies. Things tend to "just work" (once you get the code to compile) instead of silently biting you (I haven't found any gotchas or footguns within the safe Rust yet).

It does seem strange, because:

- It has constructs which aren't present in C/C++ in the exact form, like traits or pattern matching (maybe it'd be easier if they were called templates and switch, but they're not quite the same thing, so a different name is good). There's a lot of Rust-specific jargon for things that aren't as complicated as they seem.

- Some constructs (e.g. enums and empty structs) look like C's, but again aren't quite the same.

- It has powerful generics and macros, so people who want to write really clever code, can.

> - It has powerful generics and macros, so people who want to write really clever code, can.

To take a cue from the article: how do you write a generic max() function for an arbitrary number of arguments?

Someone had commented this in a response on the site.

   macro_rules! max {
        ($e: expr) => { $e };
        ($e: expr, $($rest: tt)*) => { max($e, max!($($rest)*)) }
   }
What gives you the impression that it's complicated? I can assure you that Rust contains an order of magnitude fewer features than C++, and none of C++'s edge cases or C's footguns.
Rust has a very powerful type system, and combining that with lifetimes makes for some crazy signatures that really hurt my brain.

The type system grows more powerful (and more complex) every time I revisit Rust, it seems.

I'm glad, as an increasingly powerful type system facilitates abstractions that were once painful, slow, unsafe, or impossible, but it is complex.

  > The type system grows more powerful (and more complex) 
  > every time I revisit Rust, it seems.
There have been no additions to the type system since well before 1.0. I'm curious where this perception is arising from.
> Rust has a very powerful type system, and combining that with lifetimes

If you don't count lifetimes, Rust's type system isn't really more powerful or complex than, say, that of Swift. There are no dependent types here. There are no type families. There aren't even higher-kinded type parameters.

> The type system grows more powerful (and more complex) every time I revisit Rust, it seems.

What additions are you referring to?

I wouldn't say Swift's type system is particularly simple, I rather like it, but it does have a lot of complexities.
C++ is the only language I've used, where a collection of completely reasonable decisions led to the situation that "if (a == b)" worked fine, but "if (b == a)" crashed the program due to a failed assertion.

And we weren't even using any templates!

I imagine depending on the assumptions, the same could happen in Rust, given Rust allows overriding equality too.
Most of the time in rust, you can just `#[derive(Eq)]` and be done with it, so those kinds of things are far less likely.
I fail to see how reasonable decisions lead to the situation where a == b works "fine" while b == a effectively crashes the program. Could you elaborate what the reasonable decisions were?
Yeah, this sounds more like a and b are different types but somebody messed up overriding equality operators. I wouldn't blame the language for that.
That's possible in any language that doesn't force you to include a computer-checkable proof that your overridden equality operator forms an equivalence relationship.
It's both less and more complicated than it seems. The borrowing system is quite complicated and it will take time before you can internalize it so that you can trust your code to compile on the first try. However, Rust is the good kind of complex in that when it surprises you, it tends to do so with a compiler error. It's very unlike C++ in that even when you're not a 100% sure of what's going on, the code tends to be quite robust.
Note that for any large C++ codebase, you still have to reason about things like borrows (or equivalent/similar concepts), just that the language doesn't explicitly talk about them. Rust just makes it all explicit from the get-go.

And actually, borrowing isn't really complicated, just _different_.

> I know ML derived languages well so I'm not a stranger to functional programming. And I'm a pretty competent C programmer, but it seems like Rust is about as complicated as C++, which I find offputting.

Have you used languages with memory safety and no garbage collection before?

I think Rust has no more features than it needs to support its goals, and would be interested to know what features you would like removed.

The borrow stuff is what seems to confuse or seem complicated. But if you take this guiding rule: Memory safety, no GC -- a lot of the rules just make sense and in many cases they simply couldn't be another way. And after dealing with complicated multithreaded code in C, I prefer the borrow checker. Let me know up-front where I messed up. In nearly every case where someone is saying the borrow checker is making life hard, it's often because in C, they'd have had a bug. The exceptions mainly seem to be limitations in Rust's checker.

Like any type system, there are programs that are valid that cannot be expressed. This can frustrate people when they can see that their program is OK under the current configuration of it, but there's no easy way to represent that as a guarantee. (Same annoyance dynamic-languages users might feel about static typing.)

FWIW, I prefer ML-ish derived languages, am a mediocre C programmer, and Rust seemed easy enough to learn to the point I enjoyed using it.

This article comes from a person who prefers copy+pasting to reusable macros:

"The reason why I have 15 "max" in my code is because it takes less time to copy-paste max( 15 times than it is to write a macro and think about where I should put it in my code structure!"

Am I the only one who sees a problem with that?

Setting aside for the moment that game developers may have a tendency for quick-and-dirty solutions, tomaka is well worth listening to. :) They're the author of glium (https://github.com/tomaka/glium), which all my OpenGL-using friends perpetually rave about and which showcases how Rust can be used to provide safe abstractions over low-level APIs.
Every programmer is lazy sometimes. Obviously the author doesn't "prefer" this, it's probably just a resistance to implementing a macro which they think should be present in the standard library.

If you only need to do this two or three times in your code, I can see how you wouldn't bother refactoring it, but would include it in a list of annoyances.

Yes and no. If it's a single instance (even if repeated 15 times), then it could be a net win in complexity to just leave the 15 max there – they are more obvious. This is in contrast to writing and maintaining a macro that does a trivial function, just once.
That may be your personal opinion, but 15 'max(' on a single would not pass code review in any decently maintained project, especially when there is a cleaner, reusable alternative.

Moreover, complaining that implementing such a macro is too cumbersome because it implies finding a place for it in your code structure certainly doesn't imply professionalism. Just step back a minute and imagine the fresh intern comes to you with this code and explanation. Would it fly? Or are you just finding excuses because he's the author of a well known library?

You're a bit too over-focused on so trivial an issue. The game in question is a lone developer's personal side project and isn't even open-sourced yet. Don't jump to the conclusion that a codebase written for fun and developed in isolation has any bearing on that developer's professional capabilities.
Interestingly, I think it might have. Would you rather hire a "clean code" lone developer, or a "messy" one?
That's a loaded question, I deliberately wouldn't judge a candidate based on their undisclosed personal projects. :P tomaka is a well-regarded author of many high-quality libraries, such as https://github.com/tomaka/glium . This is the output I would use to gauge his applicability for any hypothetical position.
The article is not about a game, is about what could be improved in the core language, from a professional point of view. I may write messy code in my spare time, but I don't. And I certainly don't wait for the standard library to implement trivial things on my behalf complaining there's no alternative.
If you read the OP, in literally the second comment tomaka admits that this is the least important item in the list. You characterization of his remarks as "complaining" is uncharitable.
I don’t even see why a macro would come to mind as a first solution when you can just use a fold.
That would be horribly slow.
Would it? Inlining the fold should produce the same code, with no intermediate vector. Granted, such inlining is not guaranteed, whereas with a macro it would be.
I don't know about compiler optimization but there's a bit more to it than inlining a fold.
I just tried it and sometimes it avoided the vector allocation but at least once it allocated and filled the vector, and then immediately deallocated it. (It didn't reread the data back from the vector.)
>No way to detect at compile-time whether we are in the main thread

Which languages do that? Other than requiring all functions to require a "mainthread" reference (perhaps a zero-sized type that lacks the traits to be sent cross-thread) are there ways to achieve this in general?

And... how would that be possible at compile time?
It's possible at compile time because you won't have the "MainThread" object available unless it's passed in to your other functions. And since you'll deny the traits for sending the object from the main thread to other threads, your methods can never be called.

And if you want it to be optional, like "if mainthread", then just accept an Option of MainThread.

In fact I see exactly this is proposed in the comments on the article: https://users.rust-lang.org/t/my-gamedever-wishlist-for-rust...

Oh OK, that's cool. I don't think many languages could enforce that.
I'm a little ignorant of Rust, so forgive me if this is something I should know. What is the state of bindings for OpenGL/DirectX?

Without reasonable implementations of those, I'd struggle to see Rust as a realistic option for writing games.

The interesting thing is how small all these issues are. The Rust ecosystem has really matured a lot if it can now be used to write serious software like games with only minor issues such as these. That's a pretty remarkable feat for the Rust team to have accomplished.
About a year ago I took a look at rust, but I didn't particularily like it. It enforces a single approach to programming stuff (which sounds an awful lot like the golden hammer antipattern) and lacked essential tools - notably an IDE, an actual debugger and profiling tools.

Also, for gamedev you'd pretty much need either directX or openGL bindings. And proper Windows support. I'm not sure where Rust stands on any of these things lately.

1. IDEs: Mozilla is investing into IDE support for Rust in the coming year, with the goal to have great support for two cross-platform IDEs.

2. Debugger: define "actual debugger", because Rust works great with gdb and lldb and, as far as I know, always has.

3. Profiling tools: the same tools you'd use for C/C++: perf, instruments, callgrind, cachegrind, gperftools, etc.

4. OpenGL bindings: among others, there's Glium (https://github.com/tomaka/glium), which has made a Rust convert of many a graphics programmer.