I have been developping a game in Rust on my free time for almost a year now, and I have written several libraries. During this time, I have encountered lots of problems with Rust and its ecosystem.
Usually when I have a problem I try to fix it myself (or at least open an issue), but after this post I thought that I'd share everything I encountered. Some things are more related to the needs of my own projects while some others apply to gamedev as a whole, but I didn't include anything really specific to my game.
Two things to note:
-
I didn't mention obvious things such as HKTs, plugins or cargo install. I think that these features would be awesome to have (especially plugins, I don't think people realize how plugins are a total game-changer), but I don't want to overload the list.
-
None of these are critical. They are all only missed optimisations, things that are annoying to use, things that can be bypass by hacks or with C libraries, etc.
So here we go.
Language/stdlib
-
No way to store a borrower and a borrowed in the same struct. See this link for more infos.
-
There is no easy way to cast between
(f32, f32, f32)
and[f32; 3]
(for vectors, matrices, etc.). This causes problems of interoperability between functions/libraries that use the former and functions/libraries that use the latter. -
Fixed-size arrays are missing traits. For example you can't call
.clone()
on a[u8; 50]
. Fixed-size arrays are just too annoying to use right now because of the traits that aren't implemented on them. This means that I have to use aVec
and lose some data locality. -
Unsized types do not implement
Copy
. I want to create my ownBox
-like type but that stores the data in video memory. Since this is video memory, I need to restrict the content toCopy
types. But if I writestruct Buffer<T: ?Sized + Copy>
, then I can't useBuffer<[u8]>
because[u8]
isn'tCopy
. -
There's no way to build unsized structs. If you take for example
struct Foo { val1: f32, rest: [u8] }
, there's just no way to build it. Not even withmem::uninitialized
. -
Even if you want to allocate space for an unsized struct, it is too hard to build a pointer to it. Even if you manually allocate some memory to store an unsized struct (like
Foo
in the previous point), the only way to build a*mut Foo
or a&Foo
is to transmute from a(usize, usize)
. Oh and if you do it with an unsized enum or a trait object, you will get a segfault. Right now there's no way to combine unsized structs and safety. -
No write-only references. Some low-level APIs forbid you from reading from pointers they give to you, and if you do your program can get killed by the O/S. This can happen for example when mapping video memory. Currently the only way to provide a safe wrapper around such a pointer is to provide only setters and no getters. But this is a crappy solution in terms of usability and it comes with a performance cost.
-
Loading symbols at runtime from a non-Rust shared library is too hard. Some time ago glutin switched from linking to xlib at compile-time to using
dlopen
to load it (in order to handle systems that don't have xlib). The current solution works using some hacky macros. -
There's no
min!
andmax!
macro. You need to writemax(max(max(max(max(max, a), b), c), d), e)
for example. I have a code with 15max
like this. -
No way to detect at compile-time whether we are in the main thread. This one is a bit weird, but on OS/X some GUI operations can only be done in the main thread. It would be nicer if this was detectable at compile-time.
-
No way to get the backtrace of a panic. You can recover from a panic by spawning a thread, but there's no way to intercept what is printed to stdout and write it in logs (or show a message box to the user). Games are usually not run from the CLI (especially on Windows), so if there's a panic and the program closes you will lose the logs.
Tooling
-
Missing an option in Cargo to optimize just dependencies. Right now I have to choose between a 40 seconds compile-time followed by a 30 seconds loading screen (as the loading is carried on by unoptimized Rust code), or a 1mn30 compile-time with a <1s loading screen. Having this option would give me a 40 seconds compile-time with a <1s loading screen.
-
There's no way to build a Windows application that doesn't show a console. This requires passing an option to the linker, and can only be done in hacky ways.
-
If you use a build script, everything is recompiled if you modify any file in the repository. I'm using a build script in my game, and even if I modify a file that is loaded at runtime the whole game is recompiled if I do
cargo run
. The whole point of loading files at runtime is that you can modify them without recompiling your program.
Missing libraries
-
A good maths library. There are several maths libraries, but each one has its drawbacks. nalgebra is missing computer graphics-related functionnalities because it's a pure maths library, and cgmath has a very bad API.
-
Audio formats decoders. There is currently a good WAV decoder, but WAV is not really the audio format you want. There are FLAC decoders, but all of them are either under GPL, don't compile, or are just drafts. FLAC too isn't that great. There are bindings for vorbis and libsndfile but they both misteriously crash on my Windows machine with the MSVC nightly (I suspect a bug in rustc or gcc-rs). Even if they worked, some pure-Rust decoders would be awesome.
-
A UI library that handles the layout of the UI but doesn't do any rendering. My rendering process is very optimized, so I want some kind of library that returns a list of things to draw instead of drawing itself.
-
A quadtree implementation.
- ntree - (970 downloads)
- aabb-quadtree - (273 downloads)
- quadtree - (version 0.0.0, 82 downloads, no repo / docs links)
-
A joystick/raw-input library. Initially the idea was that this would be integrated inside glutin, but since it's possible to write a stand-alone library I think it would be better this way.
-
An alternative to freetype on Windows. The Windows API provides a way to load fonts, so this could be used as an alternative to building and linking to freetype.
-
A better library for the spine format. The spine software is a 2D skeletal animation software, and to decode the exported files I wrote my own library (here is an old gif). However this library is incomplete, slow, and tied for my needs. It has already received some hacks to ignore errors.
-
A cohesive collection of AI libraries. There are a couple of disparate AI libraries around but the ecosystem would greatly benefit from a larger, coordinated effort in this space, similar to rustsim or rustaudio.
Problems that are being solved
-
It is too annoying to build for Android. Most notably the lack of pre-compiled binaries means that we can't do continuous integration on Travis for example.
- Announcing cargo-apk though the user still needs to install Android SDK/NDK (or use a docker image by @tomaka)
-
Android pre-compiled binaries were promised.
- rustup now supports
rustup target add arm-linux-androideabi
- https://blog.rust-lang.org/2016/05/13/rustup.html#example-running-rust-on-android
- rustup now supports
-
Backtraces don't work on Windows. Some debuggers, graphics debuggers and profilers crash or don't work when being shown a Rust program.
- Solved with the MSVC nightly. However the MSVC nightly can trigger linking errors which are solved by tweaking the source code of your dependencies, so it's not that great either.
-
Compilation for emscripten (or WebAssembly), while maybe not critical, would be a big plus.