The Python tutorial is a starting point to a lot of beginners. It shows one way of writing a roguelike in Rust and it was useful to validate that the Rust libtcod bindings are complete enough.
You can read the tutorial here:
And here's the repository if you want to check the code out, report a bug or submit an improvement:
Initially, I tried to follow the original Python tutorial as closely as possible, but Rust is different enough that it just did not make sense. I did strive to keep most of the code organisation and architecture close so that someone familiar with the Python tutorial could just check the differences and get going.
In the end, I'm not sure that was a good idea -- I have a feeling that if the game was designed with Rust in mind from the beginning, the code would have been more natural and showcased Rust better.
Some obvious differences from the Python version:
Rust makes it much harder to use mutable globals, so we're just
passing the extra arguments to the functions. Global variables in
Rust are done with
static, but they require
unsafe blocks which
makes them harder to use and explain.
We're using the rand crate instead of libtcod's own random number generator.
In Python, everything is a (reference-counted) pointer. So you can
define your global player object, put it into an array and still
access the player directly. In Rust, you make things into pointers
explicitly. I could have put the player and NPCs into
those pointers around and clone them into the
objects array as
well, but that was too verbose. In addition, the Python code uses
these to access parent references and Rust can't do that because of
its mutability rules.
Rust functions don't have default parameters. It's possible to work
around that by using multiple constructors, the builder pattern,
wrapping input in a struct +
default::Default, or using
Option<argument> but they are all verbose so I just opted in to
making everything explicit.
Type inference makes this much less cumbersome than say in Java or C#.
rand::random() is able to produce any type you need in your
f32, etc.). This is thanks to the compiler
knowing which type you want ahead of time. In Python you need
different functions for each return value.
indexing an array or a vector requires
usize but chances are you
want to use
i32 for your positions, dimensions etc. This can be
handled easily by wrapping the game map/level in a struct with the
right methods, but to mirror this quick & dirty implementation of
the Python guide, the explicit casting gets a bit verbose.
The tutorial is now at a parity with the original, but there's a few things I'd like to see happen. I'm not sure when I'll get to them, all these are up for grabs!
Once the support for custom derive lands in stable Rust, I'd like to switch to serde for saving/loading the game.