Hacker News new | past | comments | ask | show | jobs | submit login
Making a Game in Rust (michaelfairley.com)
418 points by b1naryth1ef on May 9, 2017 | hide | past | web | favorite | 121 comments



Great stuff, lines up a lot with what I'd care about as an ex-gamedev and spending a bit of time with Rust. One minor point:

> First class code hot-loading support would be a huge boon for game developers. The majority of game code is not particularly amenable to automated testing, and lots of iteration is done by playing the game itself to observe changes. I’ve got something hacked up with dylib reloading, but it requires plenty of per-project boilerplate and some additional shenanigans to disable it in production builds.

Lua is a great fit here and interops with Rust(and just about everything else) very well.


A long time ago I wrote bindings to Lua 5.1 (https://github.com/kballard/rust-lua), but there was a nasty problem where Lua uses longjmp() for errors (it can be configured to use C++ exceptions, but that doesn't work because you can't throw those past an extern C boundary without hitting undefined behavior). longjmp(), of course, will just skip right past the intervening stack frames, meaning if your Rust code calls into Lua and it throws an error, then unless you wrapped that call with a lua_pcall, any values that live on the stack of your Rust function will never call their destructors.

I admit I haven't bothered to research the current state of things, but how do more recent Lua bindings handle this? Does Lua 5.3 actually have a proper solution here, or do most bindings just wrap every single call with lua_pcall? I didn't do that in my bindings because I wanted to offer the full speed of Lua, but it's certainly an option.


The typical solution in C is to wrap and anchor such objects in the Lua VM so that they'll be garbage collected if an error is thrown. There are various patterns for doing this--direct binding of individual objects, or indirectly anchoring through an opaque staging struct--but that's the general idea.

Because Lua supports coroutines with a stack independent from the system C (Rust) stack, you often want to be careful mixing your stack-allocated objects. Lua 5.2 added lua_callk which allows yielding and resuming coroutines across the C API (that is, "yielding" a coroutine with a nested C or Rust function invocation).

Leveraging Lua's awesome coroutine support is one of the biggest reasons to use Lua, IMO.

Also, Lua can safely recover from out-of-memory (OOM) scenarios. On OOM it will throw an error that can be safely caught. Any Lua API which might internally allocate memory can throw this error, not just the lua_call family of routines. Quality libraries are expected to also handle OOM, which usually can be accomplished the same way as handling any other error: wrapping and anchoring temporaries, directly or indirectly, in the Lua VM.


That's still pretty much the state of the world. I think that's a trade-off that makes sense as long as you're aware that you shouldn't be depending on drop functionality in callbacks(I would hope that your initial point is dostring/dofile and let that catch handle things).

Generally I've found that any time you're interacting with an FFI in Rust all bets are off and you need to be very aware of what your libraries do and what their runtime looks like(just in C/C++).


I had intended to write a compiler plugin for rust-lua that added a lint that would ensure you have nothing on the stack with Drop when you call a (potentially-error-throwing) Lua function. But I never got around to doing that because I stopped using rust-lua (I canned the one project I was doing that motivated rust-lua in the first place).


Lua is great and generally the right tool for this sort of thing.

As an alternative, though, there are several Rust-based scripting languages that attempt to expose some of the power of the type system, etc., while being more amenable to dynamic loading, REPL, etc:

http://libs.rs/scripting/


One that's not listed there but might be a good candidate for a Rust project is miri [1] since it's both interpreted and still Rust. It's still pretty rough, but the approach it takes should perform pretty well and won't require people to learn a new language.

[1] https://github.com/solson/miri


It is outrageously fast (at least compared to compile) but would need integration via libffi to speak to the rest of the universe. This is not a priority for the maintainer, since he's exploring compile-time metaprogramming (where you want a very restricted sandbox)


Dyon (from that link) looks very cool! I'd also like to add that JavaScript is another language used in this arena, and there is a crate with V8 bindings (https://crates.io/crates/v8). Even Garry Newman (creator of Garry's mod) wrote that he believes JS would have been better than Lua for scripting: https://garry.tv/2014/08/16/i-fell-out-of-love-with-lua/


Unfortunately, some of those syntax niceties the Garry's mod creator mentioned are things that have been identified as leading causes of bugs.

++ mutates a variable in place, has non-trivial pre/post behaviour (many junior devs don't understand it), and its brevity causes it to be used inline, which results in complex one liners.

String concatenation using '+' is not considered to be a great feature in dynamically typed languages. When you can't guarantee the types of the operands, it can lead to unexpected results. Pass in two numbers, and it this returns the result of the sum. Pass in two strings, and they're concatenated.

'continue' is a strange one. I just don't use it, and I'm not sure why anyone thinks it adds anything. It's like an early return, in that it can cause some difficulty in code comprehension.

So it feels a little like Lua is being criticised for making some good calls.


> String concatenation using '+' is not considered to be a great feature in dynamically typed languages.

Yes, one of the things Perl got very right, and yet it is still brought up by people as evidence of what makes Perl look like line noise (along with == vs eq, which is the same thing, even if the problem is less troublesome in that case).

> So it feels a little like Lua is being criticised for making some good calls.

All too often languages (and aspects of them) are criticized mistanely as being worse when all that is presented is how they are different. All too often we vilify the unusual just for being unusual.


As to 'continue', starting with Lua 5.2 you can actually simulate it perfectly with a

  goto continue
and then a

  ::continue::
slapped immediately before the relevant loop's end. Surprisingly, it's not affected by the rule forbidding "going into new variable scope" - because "the scope of any variable ends before 'end', wink wink, nice trick we left here for you no?"


Yeah, Lua certainly has it quirks. However binding to JS is a huge endeavor. For instance that v8 crate doesn't easily expose a way to call Rust from JS which is trivial in Lua.

Also the JS runtime is huge and the build system is complex. You can run Lua in ~400kb or less and LuaJIT tends to walk over any other jitted interpreted runtime.


While the performance definitely doesn't compare to LuaJIT, Duktape (http://duktape.org/) could be a good target for Rust bindings - it's fairly small and available as a single-header/implementation library.



He even invoked Wadler's Law!

It really is too bad that JavaScript is different from Lua. Lua predates it by 2 years. [1]

Criticizing a language on syntax is something I would hope we could move past. Mr Newman didn't read about the history and purpose of Lua, otherwise he would know it was targeted at scripting data loads for simulations written in Fortran. Hence the 1 based indexing. Lua syntax is one of its SELLING points.

[1] https://www.lua.org/versions.html


It's really too bad we don't use Lua in the browser...


I largely echo the sentiment of using Rust for game development. The world doesn't need another Flappy Bird clone but that's what I wrote because I ended up porting a Go version that was using SDL originally by Francesc Campoy from the Golang team: https://github.com/campoy/flappy-gopher

I was able to build the Rust version fast, and the SDL library is actually quite usable/stable for most things.

Flappy-Rust has particle effects, the beginnings of parallax-scrolling and basic collision detection. The rust code ended up being pretty reasonable however I'm quite sure there are a few places I could have simplified the sharing of assets.

If anyone is interested in this space check out my repo: https://github.com/deckarep/flappy-rust

Also please see the README.md where I talk a bit more in-depth about how the Rust version differs from the Go version.

Here is a .gif preview of the first iteration of the game: https://github.com/deckarep/flappy-rust/blob/master/flappy-r...


It would be nice to have a minimal gameish program as a example for people learning Rust.

When I teach kids javascript I start with an etch-a-sketch. It's aided by a simple library to hide the mechanics of the HTML canvas element, context, etc. This allows it to be small enough that they can view it all in one go and build upon it.

    print("Draw with the arrow keys");

    var cx=320;
    var cy=240;

    function update() {
      // the arrow keys have key codes 37,38,39 and 40
      if (keyIsDown(38)) {    cy-=1;  } 
      if (keyIsDown(40)) {    cy+=1;  }
      if (keyIsDown(37)) {    cx-=1;  }  
      if (keyIsDown(39)) {    cx+=1;  }
  
      fillCircle(cx,cy,6);
    }

    run(update);


There might be merit in writing one of these in every language (and a companion that uses the mouse), maybe placing it on github . With a really simple program like this you can focus on learning the language while making something. It's a tough job figuring out how to learn a language while simultaneoulsy learning how to write the boilerplate needed to get something onscreen.


For simple pixel access and no primitives, https://github.com/emoon/rust_minifb is very compact and neat.

Otherwise rust-sdl2 is batteries included, there may be more ceremony than html5 canvas but it's easy to hide behind a simple interface. It has many simple examples.

But, like with C++, as long as the language doesn't come with a standard graphics library, I doubt the "official" books can really use graphics apps as a learning tool.


Easy to hide behind a simple interface if you already know the programming language. My suggestion is that someone perhaps should do just that so that beginners don't have to learn how to construct a simple interface at the same time as they are learning the language.

The MiniFB code does seem to be a good starting point for this sort of thing.


Yeah, I was just echoing your comment that you provide a small library for HTML5 canvas-based teaching. If I was going to teach someone Rust, I would definitely do that, but of course I (the teacher) would need to know enough to build it in the first place.

I absolutely advocate using graphics and games as the hook to keep people interested in learning. [1] "The immediacy of many 8-bit computers was awesome. Instant boot to a graphics-ready command line and program editor. I was instantly hooked". And that's why, despite the really bad fit, I support the addition of a 2d std library to C++, and would do likewise for Go, Rust, etc.

[1] https://twitter.com/TheJare/status/860094027370291200


It's not a graphical game, but the introductory project in the book has been the "guessing game" for years now https://doc.rust-lang.org/stable/book/guessing-game.html


Yeah, it's the dynamic interactivity that I was looking for. I'm afriad console IO isn't a starter here. I'm sure it's fine as a console IO example, but it's just a completely different class of thing.


I'm actually a huge proponent of learning through text type games in lieu of more complicated graphics related black boxes where someone is essentially letting a library do 98% of the work. A text game is literally as simple as it gets while requiring no modules (well maybe IO or system if your language doesn't include it by default). Guess my number also is seems more mathematical although that is just subjective rubbish on my part ;). I do believe you're correct that certain people would like a graphical introduction more. Maybe you could write a cool intro to Rust with graphics!


I think the issue isn't text versus complicated graphics, it's that the only thing you can do with most standard libraries is buffered line I/O, which doesn't feel like directly controlling anything. If you could just read the actual keyboard and poke some letters into a 2D text array, it would enable a whole different kind of interaction.



Polling the keyboard and using something like ncurses can work. I've implemented console Tetris in an hour using just those tools, and you get to write a game loop, rather than being called back by a framework like most GUI setups.


> The include_* macros are great for “packaging”. Being able to compile small assets directly into the binary and eschewing run time file loading is fantastic for a small game.

For C/C++/etc devs looking for something similar, BFD objcopy supports an "-I binary". It will emit an object file with _binary_objfile_start, _binary_objfile_end and _binary_objfile_size symbols.

But I have got to say that making it a language feature means that Rust is truly a batteries included language.


Agreed. I code in C# and there's facility for bundling assets into the binary but they're handled at the project file level and not the C# file level, and this means the keys to access this binary content is not available as a compile-time constant. You're still just passing strings around and hoping they match.

Having first-class language support for resource files looks fantastic.


> and this means the keys to access this binary content is not available as a compile-time constant

Isn't that exactly what resource files give you or am I confusing something? Anything you add in resource files have are accessible as a static variable.


It looks like you're right (for C# at least): https://msdn.microsoft.com/en-us/library/7k989cfy(v=vs.90).a...


I wonder how XNA's asset pipelines worked. I also wonder if you could "pack" a binary into an assembly by writing a tiny bit of IL around it.



Yeah, that's the workflow I was mentioning, cooking the file into a resource by referencing it as resource in the csproj, but then it's keyed by a string of the file-path within the project file, which isn't nearly as nice as having it bound to a variable in C#.


There is also an option to use assembler or inline asm. I found quite a nice utility that uses inline asm [0]. It's widely portable and I think that I will use it instead of my naive asm/shell combo that doesn't work with mingw asm.

The problem with objcopy outside of unconvenient usage and naming is that naive objcopy will result in your binary having executable stack [1]. You can change a symbol name, but that's also unconvenient.

Check resulting binary with:

  $ readelf -lW the_binary | grep GNU_STACK
    GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE  0x8
  $
Notice: RWE instead RW.

Also: https://wiki.gentoo.org/wiki/Hardened/GNU_stack_quickstart#H...

[0] https://github.com/graphitemaster/incbin

[1] https://news.ycombinator.com/item?id=10816322#10818085

EDIT:

My shell script - bin2o.sh:

  #!/bin/sh
  set -e
  
  filename="$1"
  name=$(echo "$1" | sed "s/[^A-Za-z0-9]/_/g")
  obj="$2"
  
  echo \
  "	.section .rodata
  	.global ${name}
  	.type ${name}, @object
  	.global ${name}_size
  ${name}:
  	.incbin \"${filename}\"
  1:
  ${name}_size:
  	.int 1b - ${name}
  	.section .note.GNU-stack,\"\",%progbits
  " | gcc -x assembler -c - -o "$obj"


To embed binary assets into any language, write a program that emits the binary as a string containing hex codes.

E.g. bytes [0, 12, 99] would become "\x00\x0C\x63".

Almost every language has facilities to treat a string as a set of bytes, so this works basically everywhere. It's nice because you don't need any special language-level support for it.


While I do agree that it is possible to do something like that, I think we can all agree that the following (available in Rust) is soooooo much nicer:

    let stuff = include_bytes!("my.file");


While include_bytes is nice, xxd -i in a makefile is pretty workable solution in the scale of things.


Fair point, however I believe the OP made the game work for many different OSes, and I don't think xxd or Makefiles are the best when you have to deal with Windows (whereas Rust's include_bytes! provides a platform-independent solution).

I had never heard of xxd until your comment though, so thank you very much!


Somehow I'm reminded of the old adage "UNIX is IDE for C"


Now I have to rewrite my arcade game emulator in rust just so I can use that to load rom images...


include_bytes! is for compile-time inclusion of files though, not for runtime.


Yeah but when it only runs 15 games with a max size of 32K... But yeah it makes it non-distributable because it would then include the images.


[flagged]


Please don't comment uncivilly, regardless of what you think of someone else's comment.

https://news.ycombinator.com/newsguidelines.html

https://news.ycombinator.com/newswelcome.html


C++ string literals usually have fairly low maximum length (C++ standard suggests 64K in [implimits/2.16]). Initialized arrays can be longer in practice but they also have a limit (the same standard suggests 16K but even that will give you 128Kb if you initialize uint64). Objcopy, as suggested elsewhere in the comments has more comfortable limits.


That has also been a feature of Windows for quite some time, you have your IIRC .exe icon and various string resources packaged in the .exe itself. It's pretty useful on the whole.


I wasn't even aware that you were allowed to ship Rust code in iOS apps, I thought that Apple had a whitelist of allowed languages?

EDIT: And for those interested, you might want to check out Rust's recent inclusion in a AAA title: https://www.reddit.com/r/rust/comments/69s225/rust_makes_it_... :P


Rust as an art asset! The dimly lit room, the red glowing Rust code on the wall, the main character with a large gun and the commenter saying that requires and unsafe block.

The Reddit comments are great.

Edit - Could you tell me why you downvoted me. This wasn't even the slightest bit argumentative, so I am not sure who I angered.


I didn't downvote you, but I also don't understand what your comment even means. Dimly lit room... what? What Reddit discussion are you referring to? What does your comment have to do with writing games in Rust?


They're commenting on the link in the GP about "Rust's recent inclusion in a AAA title" (some rust code is used as text in a generic 'computer code' image)


This tells me why I got downvotes, I replied to the wrong comment.

The game wasn't written in rust, is was used in a texture and drawn on a wall.


The game that's the subject of the OP is written in Rust. The one in the Reddit thread linked elsewhere was only a texture.


But I think we can all agree that if we are programming a gun there should be an unsafe block.


Really? How do they know what the representation of the object code was before compilation? Seems like if they had a whitelist it would be difficult to enforce.


The biggest problem I faced with Rust and macOS/iOS is related to Apple's requirement for bitcode on all submitted code to the App Store for Watch and appleTV applications.

This internal Rust discussion focuses on it for more details: https://github.com/rust-lang/rust/issues/35968

This gist is that Apple is requiring bitcode, but isn't giving easy access to the LLVM version they use for their own tools. This means there's not a path forward to support bitcode generation from Rust that would align with Apple's requirements. This currently only effects iOS on Watch and TV, meaning you can easily target macOS and iPhone without issue, but I fear that the writing is on the wall.


I continue to be amazed that apple is not considered as having a monopoly on "stores for apple devices".

These are exactly the kind of archaic requirements that would be a nonstarter or otherwise kill market support for a store given any actual competition.

Instead, they are able to leverage it to try and push their 'approved' languages and developer environments - furthering anticompetitive lock-in.

Apple making money from selling devices (or even distributing "at a loss" devices which they benefit from having exist so they can better act as software vendors) and them making money from their store are two separate revenue streams, after all.


"products made by one brand" isn't a category of products. If they're the only brand that makes products for a particular category, then they're a monopoly, but you cannot simply define a category as "stuff made by that company", because by that logic, every company is a monopoly on stuff made by them.


Actually, a makers own products are a distinct market for antitrust purposes if people empirically don't substitute out of it, as shown by the producer having market (pricing) power.

I wouldn't be surprised if that's true for Apple for some of its offerings.


It's not. The only product you could even try to make the argument for is the iPhone, but the generally-accepted categorization here is that iPhone and Android phones (and Windows phones) are part of the same category, which makes sense because people absolutely do switch between them.


Actually I have a better argument against this than my other.

No, "products from brand A" is never a category. However, a company may create a brand new market with a product, and they may be the only company with a product in that market for a while. But that still doesn't mean the category is "products by that company", it just means it's whatever new category was created from the product.

For example, the iPhone arguably created a new category of smartphones. But competitors quickly introduced their own products in this same category (e.g. Android).


I imagine it would be easier to accept your assertion of anti-competitive behavior if it weren't trivial to completely ignore Apple for your phone needs, or if there were some sort of Constitutional right guaranteeing you the Apple phone of your dreams


It's not as bad as it sounds. The LLVM they ship in Xcode is basically stock, or at least it was in past releases where they posted the source - after all, Apple is the upstream for LLVM. And unlike the LLVM API, the LLVM bitcode format is stable and preserves backwards compatibility whenever possible, in the sense that newer versions of LLVM can read old bitcode. So there's a good chance that passing bitcode from rustc to Apple's tools will Just Work, and if it doesn't (like if rustc is using a newer LLVM than Xcode) then it should be fixable.


I agree that ostensibly it is possible. But what I would love is to see either: Apple show how to do it in a supported manner; or someone at least prove that it's possible. So far I haven't seen either.

To your comment about bitcode being stable, I don't think that bitcode stability and forward compatibility was something guarateed until 4.0.


I mean, it's certainly possible for simple cases. I just tried:

    % cat test.rs
    fn main() {
        println!("Hello, world!");
    }
    % rustc --emit llvm-bc -C lto test.rs
    % LIB=$HOME/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib
    % xcrun --toolchain XcodeDefault clang -o test test.bc ~/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/x86_64-apple-darwin/lib/liballoc_jemalloc-ea49ffd3fee5264c.rlib
    warning: overriding the module target triple with x86_64-apple-macosx10.12.0 [-Woverride-module]
    1 warning generated.
    % ./test
    Hello, world!
    % rustc --version
    rustc 1.18.0-nightly (91ae22a01 2017-04-05)
    % xcrun --toolchain XcodeDefault clang --version
    Apple LLVM version 8.1.0 (clang-802.0.42)
    Target: x86_64-apple-darwin16.6.0
    Thread model: posix
    InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
That's using Apple's compiler to compile bitcode from rustc. The .bc file has significant chunks of Rust's libstd embedded, so the test isn't as trivial as it seems. To embed bitcode as they want for the App Store, you can use -fembed-bitcode, except it doesn't work because liballoc_jemalloc wasn't compiled with that option (but that's trivial to fix).

I also tried compiling for iOS, which seems to work, but I didn't bother to test the resulting binary.

(As for stability, according to the announcement[1], the previous policy was that bitcode would be readable "up to and including the next major release", which was already reasonable from the perspective of keeping a third-party compiler's output compatible.)

[1] http://blog.llvm.org/2016/12/llvms-new-versioning-scheme.htm...


That's an excellent example, thank you!

I wonder if the dependency on jemalloc can go away once custom allocators stabilize. Then the macOS system allocator could be used.


You could get rid of jemalloc, but on the other hand you could also just compile it with -fembed-bitcode. (It's part of the Rust standard library, so that's a bit more work. Rust code doesn't have this issue because .rlibs contain serialized ASTs, and passing -C lto makes rustc only use that rather than use the precompiled bits. In any case, it's just a matter of rebuilding.)

Incidentally, I just tried updating my nightly Rust, and it stopped working - clang started failing to read the bitcode, bailing out with a vague "error: Invalid record". This is not too surprising, because Rust just landed a big LLVM upgrade two weeks ago. Newer LLVM can read older bitcode files but not the other way around, and even though LLVM 4.0 was released months ago, Apple seems to only sync with trunk yearly, along with major Xcode releases. (You can tell based on the --version output.)

However, Rust can be built against an external LLVM (rather than the fork it uses by default), and AFAIK it tries to preserve compatibility with older versions. So it should still be possible to use the latest rustc, you just have to compile it yourself against a slightly older LLVM.

edit: corrected thinko about .rlibs


Note that I've obviously never written an iOS app, but I figured that Apple required you to submit the source of your app for their auditing process. Which sounds extreme, but this is Apple we're talking about. :)


There was indeed a rule at one point that apps had to be written in C, C++, or Objective-C. This was allegedly implemented in order to ban cross-compilers such as Flash compiler[0]. Presumably they would either require the source code to verify this, or inspect the compiled binary for some identifying feature. However, these rules don't exist in the current version of the guidelines[1].

[0] http://daringfireball.net/2010/04/iphone_agreement_bans_flas...

[1] https://developer.apple.com/app-store/review/guidelines/


This rule got so much press 7 years ago that it will be one of those "truths" about a platform that never dies on internet forums, that and the Playstation platforms using OpenGL as their primary API...


I have submitted an app in the past and no, you don't have to submit the code.


For a brief time they had restrictions on the languages you could use. That was a reaction to Adobe shipping a tool to compile your Flash source into an iOS app. But that didn't last very long.

That said, if you're dynamically interpreting code at runtime that was downloaded from the internet, you're only allowed to do that using JavaScriptKit. You cannot, for example, embed the Lua interpreter and then interpret code downloaded (but code in your app bundle is fine).


> I had two or three false-start attempts at learning Rust where I’d spend a few hours with it, not get anything done, and put it down until a few months later

This sounds (sort of) encouraging. I was kind of expecting to learn it by putting in an hour here and there e.g. 2-4h/month. But I'm beginning to think that might not cut it...


Go is a language that you can absolutely learn like that, and it's pretty amazing. (Python, too)

Rust ... isn't. I love Rust, but you do need to put some dedicated time into learning it. We're hoping to improve this!


It's unfortunate that our profession emphasizes ease of learning so much. There are things you can make better to make a language easier to learn that don't affect usefulness, but at some point those mostly run out and there is a trade-off between making something easier to learn and easier to use. One brings more new people into the community, the other retains them by continuing to provide good value for invested time.

Python is actually a good case for this, where choices to make the language simpler and more uniform led to knock-on effects that ultimately lead to aspects of it being quite cumbersome (IMO, obviously). For example, how anonymous functions are specified, how cumbersome and limited that makes map and grep, and how that leads to list comprehensions, which for all but the simplest cases are much more complicated to read and write than a simple chain of maps and greps (assuming something better than lambda).

At the other end of the spectrum you have APL and its relatives. Lots of work to learn and become proficient in, but then you can read and write terse, performant and versatile programs with it quickly. Of course, the pool of people that can understand what you wrote is orders of magnitude smaller than with some of the easier to learn languages.

I understand the urge to make Rust as accessible as possible, and I applaud that effort. I just hope that Rust continues to walk the line of accessibility without compromise on the core ideals.


I don't think the intent is to compromise on these :)

But lots of things can be made easier to learn via better tooling, better docs, or better diagnostics. Or just removing papercuts.

One such thing I've recently been thinking about is https://github.com/Manishearth/rust-clippy/issues/1737 , which would be cool if it got big.


Oh, that would be nice. :)

Rust, being largely of new things (in implementation and and presentation, if not theory), has plenty of room currently to explore changes and features that have little or no negative aspects. I'm a little worried about what happens when the low hanging fruit is fine though.

That said, rust has been very good so far at assessing the merit of proposed changes. I recall a few months back that reasoning was explained, and it assessed positive and negative aspects on a few well defined axes.


Whilst an easy start is nice, any language that you can pick up with no effort just means you're learning the same language + paradigm that you already know with a few differing features (in go's case CSP). Nothing wrong with that, CSP is awesome etc

I'm not a rust dev (definitely interested in picking it up though), but heavily into clojure and learning haskell slowly on the side. I hear this complaint a lot from people who think that because they learnt C#, Go, Java and Swift in a weekend that all languages will be just as quick to stick in their heads.


A lot of people have to unlearn "classic" OOP inheritance in order to learn Go. That's a significant change in paradigm.


I don't think this is necessarily true. Go did introduce some paradigm shifts, but they were gentle and easy to ease in to.

Also, I mean, sure, the language may not have new things, but ultimately the goal is to use the damn thing, not learn new concepts :) Go is amazing for that.

I totally agree that expecting to learn a random language in a weekend is unreasonable.


What Go borrowed from CSP - channels - is definitely awesome. But it was Go's interfaces that was the real game-changer for me. Very under-appreciated.


Yeah I'm guessing it's a treshold you need to get over that requires at least a bit (a week, two weeks, a month) of doing it more than sporadically. Wish I had a week I could do 40h rust in, but that's sadly not the case. I'm doing baby steps with Rust now but it almost feels like the language is walking away faster :)


I would love to read a detailed explanation of the packaging process for iOS and Android.


The author should seriously consider factoring out his build scripts and start a crowd-funding campaign to open source it for X thousands of dollars. Hell, I don't even know Rust and I'll donate ten bucks.

The five platforms support is bloody impressive.


The author explains the process briefly here: https://gist.github.com/michaelfairley/d86137085307d2e5c16ee.... I think the most obvious pitfall is the native API calls for both platforms. Maybe a cross platform framework like PhoneGap for Rust?


Neat! I was just complaining about the lack of multiplatform Rust games proving out the concept. If only there were some consoles or handhelds on the list... although iOS/Android being on the list is somewhat encouraging.


The performance on my Android phone (Nexus 6) is not good. I would have thought Rust would be fast. The fps is fairly low, a lot lower than Maps or Chrome, at least when Maps isn't freezing up. And it seems the transitions between levels might be proportional to fps, because they are irritatingly long.


I'd guess any performance problems are probably on the GPU / OpenGL side of things, which Rust won't help with (nor hurt). The Nexus 6 has a 1440p screen powered by a mobile GPU maybe a little weaker than previous generation consoles (~172 gflops max for the Adreno 420, vs e.g. ~220 for the PS3 [1]) that were often handling 720p titles (a quarter of the pixels). Very easy to exceed your frame budget.

[1] http://gpuflops.blogspot.com/


About floats implementing PartialOrd and not Ord:

I have not tried out Rust yet, but couldn't this be solved by wrapping the float in a single-field struct that checks for NaN on construction, implements Ord using PartialOrd and otherwise passes everything through to the ordinary float inside?

If this isn't possible, I'm definitely interested in the reasons.


Every calculation will have to check for NaN, so it's a non-starter when you need a lot of float calculations.


The problem was in the context of looking up min_by_key with the key being a floating-point number, so I think the only NaN-check necessary should be for that key. I did not intend to imply that it should be done for all calculations, only those that need totally-ordered floats. (Or if you are really sure NaN will never happen, you leave out the check altogether, and claim total ordering anyway.)


I guess that is a particularly bad use case of specialization, which itself is proposed as an RFC [1] and approved for implementation. It would look pretty similar to, say, C++'s `vector<T>` and `vector<bool>` split, because it does something more than what it should do. Probably the better solution is to merge PartialOrd and Ord, as the benefit from having two traits has been always unclear (I personally have no strong opinion against or for that though).

[1] https://github.com/rust-lang/rfcs/blob/master/text/1210-impl...


If the type proved there could be no NaN, why would the check be needed?


0.0 is a finite number that yields NaN when divided by itself. The initial checking is not sufficient.


So check on construction, when doing operations with other unchecked floats, and when dividing. Any other operations that could produce a NaN?

Wait, are +-Inf considered unordered? Edit: Nope. inf == inf, so you should only have to worry about NaN. https://is.gd/Xi5jdr


If you want the full list: 0/0, ±inf/±inf, 0 x ±inf, ±inf x 0, inf + (-inf), (-inf) + inf, inf - inf, (-inf) - (-inf). This list is large enough to make NaN checking inefficient.


Thanks. Yeah maybe not.

It seems like the right way is to add min/max fns to the Iterator trait for PartialOrd that ignore items where partial_ord returns None.


Not all partial orders have well-defined min/max, though. Ideally, you would want traits for meet/join semi-lattices, but I'm not sure how much abstract algebra you can inflict on the average Rustacean. Then there is the whole problem of NaN is incomparable with everything, so you'd have to use a non-standard definition of semi-lattice to allow for that.

And do you want max(NaN, 0) to be NaN or 0? There is a case to be made for either possibility.


If I'm writing a game I don't really care if one of many operations accidentally resulted in NaN I would like to treat NaN like SQL treats NULL: pretend it doesn't exist. I can't glean useful information out of it, might as well try to get something from the rest of the data.

Then again according to float semantics, sum of a list containing NaN must be NaN, so it would make sense to extend that principle to min/max.

But the naive implementation of min/max will never output NaN because NaN always compares false. (... unless NaN is the first element. ugly.)

Maybe making it explicit would be best: Make an explicit fn for float collections that filters out NaN in an Iterator, then claim that Item is totally ordered. I'm too much of a rust noob to know if this is possible. (playground here I come!)


It is an interesting educational project but staying away from Unreal or Unity3D is really a tough decision if your project is going to need some more features that are already developed and well tested in one of these engines.


In my experience, the real value of the engines aren't "features", but tools oriented at level designers and artists. Once you have a project with 10 or more members, managing art and assets becomes a real headache - and most of homegrown solutions, as well as open source engines start to fall apart at this point.


Great writeup. Eye opening that you can actually write iOS apps with rust. w00t, totally trying this for my next app. (It's a bit overly bit expensive for my liking but otherwise I would have given your game a shot).


Is there a write-up on how the rust code ends up being embedded in the iOS app?


that's a great twist on the snake game. does rust compile to wasm?



I want to do an experiment with object oriented code that generates view code in wasm and keeps other code in the server.


Screenshot, please!


Hit the second link in the article for a video: https://m12y.com/a-snakes-tale/


Did a double take on the title.


This is probably not a popular opinion here in HN but why does it matter what language you use to make your game in? You can use virtually any language to make a game. From my point of view the best language for a game is that which makes you the most productive for cranking out that code. And we all have our own personal preferences about which language is best, which I think is fine, you should code with the one that you feel most productive with. At the end of the day users will not be able to tell the difference. All that matters is whether your game is fun or not.


You can use any language, but it's not like they are all equal and have the same tooling available. Rust has some distinct features and there is a natural lack of resources as the language is still new, so it's interesting from a developers perspective that someone went through the work of doing all that from scratch and made it to the end.

Also it's an alternative to C++ which is heavily used in gamedev. I personally believe Rust can shine in gamedev and every experiment is interesting.


> This is probably not a popular opinion here in HN but why does it matter what language you use to make your game in?

To the end user? It doesn't matter one bit.

To your peers? It's interesting and educational to talk shop.


What matters is not "what language you use to make your game", it's "which kind of software you can make with language X". That is, the focus of this article is not the "making a game" part, it's the "in Rust" part. It talks about where Rust helped, and which were the pain points. These are things of interest for Rust developers, in both senses: people using the Rust language, and the ones who develop the Rust language.


The language he chose doesn't matter except to him, but writing about his experience using it does matter to anyone interested in choosing a similar path.


It doesn't. But this article is interesting (rare/novel) because it is about Rust, not because it is about making a game.


100% agree with you. However - posts like these might give encouragement to people who know a language that they can actually accomplish Project XYZ in said language, especially if there's accepted norms about what types of work a language is "good for".


Although I did not point it out, I'm talking about experienced software/game developers. A novice can definitely make the mistakes you are mentioning, which makes your comment a valid point.


Replace "game" with "software", and your post is equally valid.


As long as it is not flash! Some serious flash hating here.


Eh, AFAIK Flash's big problem is the implementation - it's horribly inefficient and insecure etc, but in theory someone could make their own clone of Flash and make a much better version that works well for all that stuff.


Just have to say Flash and its an automatic down vote. As some background, I used to program 3D engines in C++ for 20 years. So I know a little about the subject, and I hated flash back then too, but just out of ignorance.

first point - insecure

Would you rather have a safe that thousands of thieves try to break on a daily basis, or would you prefer that only casual thieves try to break it every once in a while?

I would trust my money to the safe that has been thoroughly tested. Checking for safety is pretty much all the flash team does these days.

inefficient

Its actually as or more efficient than JavaScript. Where there lots of crap flash ads and programs? Sure, but that is because Adobe made it easy to create flash apps.

I am writing a 3D engine in Flash that displays and updates millions of polygons at 60 frames per second. I can't do that in JavaScript yet.




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: