I’ve been playing with Rust some more lately, and I’m starting to really appreciate a few things about the language.
I’ve been going through Hands on Rust, and building the little Roguelike game the book lays out. I’ve learned how to use Cargo, how modules work and how to stop worrying about macros. It’s been a very enlightening experience, and I’ve enjoyed it immensely. I have also been working on an implementation of the DCPU-16 computer using Rust, though I’m not quite done and still need to implement interrupts.
Expressions
Something I’ve really loved in Rust is that nearly any block of code can return a value. For example, instead of a ternary operator, I can just use a plain if statement
let a = if b > 10 { "abc"} else { "cba" };
Similarly, I can use a match
statement to do something similar
let a = match b {
Ok(z) => z.foo,
Err(z) => "bar",
};
It allows for some concise, but still readable blocks of code without memorise too much syntax.
The Compiler
Rust’s compiler is incredible. Still being a rust newbie, the error messages are incredibly detailed and useful, even
offering good suggestions on how to fix things. For example, from habit I tried to return a &Machine
instead of Machine
type from a function. The rust compiler told me the following
error[E0106]: missing lifetime specifier
--> src/machine.rs:39:21
|
39 | pub fn new() -> &Machine {
| ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
help: consider using the `'static` lifetime
|
39 | pub fn new() -> &'static Machine {
| ~~~~~~~~
Which was helpful in that it gave a fair bit of insight into lifetimes. If you forget a semicolon, it can usually figure out what you meant. One of the things that always frustrated me about C and C++ was the lack of compiler support before you shot yourself in the foot, and Rust’s compiler has been built in a way to stop you way before that happens.
Explicit mutability
I kind of questioned the point a little bit when I first started learning Rust, but I realised as I started implementing my own code that it became much easier to track what functions mutated state and which did not.
Semi related to something at work, I implemented a library to do maths with some generated protobuf structs. I wanted
to implement the functions as pass by value so that the original structs would not be touched if someone went through
later and made modifications, but ended up having to do pass by reference because the generated objects had sync.mutex
which the linter didn’t like. Had I had Rust’s mut
keyword, I wouldn’t have had an issue.
Cargo
The cargo package manager is also brilliant. Being able to choose features to compile into your application and
easily choose the architecture you want to target in the cargo.toml
file makes for a very nice developer experience.
Something that could be a little better
One thing that has tripped me up a couple of times has been trying to use a u16
type as an array index and encountering
compiler errors because you can’t index an array with that type. Which is annoying, the compiler suggests the answer
is to cast to usize
, but it can’t just do it automatically. I get why, but it just leads to some gross code.
All in all, my experience has been incredibly positive. I’ve very much enjoyed learning Rust, and it’s made me rethink how I code in go a little bit as well, particularly when thinking about unintended side effects of functions. Hoping I can use it some more in the future.