Categories
Learning Programming

Language of the Month: Rust

I found it hard to believe, that it’s been 4 years now since I finished the previous installment of “language of the month” column, in which I pick a programming language and dive in for a month to see something new. In that 4 years I have learned a lot of programming for sure – though probably very little computer science, and barely any new languages. It’s time to chance, and for the revival of this I’m checking out Rust.

A bit about the LotM first, though. I was previously learning Javascript, Prolog, Lua, and Scala. Each time aim is to go beyond the tutorials and example code, and do something more that lets me discover the the language’s real face – strengths and weaknesses included. At the beginning of the month announcing the language like in this post, and at the end of the month in another post recap what have I learned.

Getting started

First start with a confession – I actually wanted to try out “the language that John Carmack is playing with these days”, but then messed up since that is Racket, not Rust. I guess I’m not the only one making this mistake, and never mind, that will be good for another month! I guess maybe Rust has enough mindshare on Hacker News to stick in my mind.

So then, Rust is a systems programming language that aims to be safe, fast, and play with threads nicely. Its Wikipedia article has a bit more too. Searching for books, tutorials, examples, and projects that use it didn’t turn up as many sources as the previous languages I tried (though it’s much younger language too, so that’s only fair, I guess). On the other hand, it has an extensive official book, so that was a good place to start.

First Impressions

So far I’ve spent two half-evening with Rust, besides collecting the learning material I went through the first few sections of the book (no I’m in the middle of section 4, Effective Rust).

Setting up Rust was pretty simple, I got to know Cargo, its package manager (and sort of build system, and sort of tooling, I guess), and did code the guided examples (both the binary and library ones), though the testing and documentation sections just read through.

// FizzBuzz not from the docs
fn main() {
    for num in (1..36) {
        if num % 15 == 0 {
            println!("Fizz Buzz");
        } else if num % 3 == 0 {
            println!("Fizz");
        } else if num % 5 == 0 {
            println!("Buzz");
        } else {
            println!("{}", num);
        }
    }
}

So far it’s relatively straightforward, interesting, but I have seen very little where its strength lies. Having first class concept of documentation in code, directly including tests, debug & release builds by default, easy project setup boilerplating are good, though I guess those sort of should be default for every new language as “best practice”.

What I’ve seen a bit instead, is quite a bit of arbitrariness in language. I do accept that it would be strange if first time seeing a new language  everything made sense, and try to cut it some slack. Still, for example why is there so much mixing between lowercase and uppercase type names, as well as abbreviated and not abbreviated versions, eg. String  and &str ? I wonder. Having seen a few expression compositions in the tutorial projects, they also feel quite arbitrary (ie. the method names don’t give a good idea what they are supposed to be doing – why .unwrap() after a thread .join() ? Will get there, but it’s hazy at the moment.) That might be just the issue of the tutorials, so I give that time too. And try to remember, that even though e.g. Python feels so “homely” now for me, there must have been some time when it wasn’t. Curiosity will help with this, and Rust has my attention.

In the meantime onwards with the documentation, and I’m looking for some project ideas and others’ existing projects to look at (suggestions are welcome).

14 replies on “Language of the Month: Rust”

It would be interested to see how you see rust during your learning sessions. I come from the embedded world and am quite excited of a language that is designed for safe operation and fast. Both aspects are not typically complementary.

The order in which concepts are introduced in the tutorial may not be the best. In case it helps, here are answers to two of your questions:

1. They really need to explain the relationship between String and &str better. Basically:

a. Rust’s borrow syntax (the &) is more or less a compile-time checked version of passing pointers in C

b. “String” is a heap-allocated string and “&String” is a borrowed reference to it.

c. “&str” is a slice (a reference to a string or section of a string which may live anywhere in addressable memory)

d. A slice only has meaning in the context of a string, so “str” can’t be instantiated because it would be allowed to outlive what it references.

e. “&str” does everything “&String” does and more (it can reference strings not allocated on the heap), so there isn’t much call for “&String”.

In other words, the String/&str split is basically an unfortunate side-effect of “Special cases aren’t special enough to break the rules.” from the Zen of Python.

2. You have to unwrap() after join() because Rust has algebraic data types (tagged unions) and uses them for monadic error handling instead of try/except.

join() returns a Result which can be Ok(T) or Err(E) and unwrap() says “get me T or panic!() (terminate this thread with a traceback)”.

The Rust docs explain that the .join() will return an error if the joined thread died with a panic!().

When you get to the `match` expression (Rust book, section 5.13) and chaining methods on Option and Result, it starts to look very beautiful.

(Just for the Option/Result method chaining, think jQuery’s monadic API with CoffeeScript’s existential operator and concise lambda syntax and a compiler which optimizes it down to something as efficient as what you’d write the older way in C)

Thanks a lot for the notes, these are the things that I kinda picked up as well along the way!

Yeah, I find the docs a bit lacking and sometimes misleading. Quite often when I wanted to do anything even slightly different from the example given I ended up running into an issue that calls for a whole different approach of implementing the original example. I guess the language is moving really fast, and time is spent more on development than documentation. It’s mostly up to the users at this point to do the documentation

Also wrote up the results of this month’s learning at https://gergely.imreh.net/blog/2015/12/lotm-rust-results/

You will see `.unwrap()` pretty often(in examples). It has nothing to do with `.join()` in particular but more with the fact that `.join()` might fail and it returns a “Result” which you can handle or “unwrap”(which means: I don’t want to handle this, just crash if I receive an error). I recommend reading on “Option” and “Result” because these types are used very often in Rust to represent operations that can fail(Result) or that might not return a result(Option).
I don’t want the post to be to long so I’ll reference you to the best detailed blog post you can currently find about this: http://blog.burntsushi.net/rust-error-handling/
It’s very extensive, even explains some error handling design patterns in Rust.

upper lower case convention is like python, the only words that have upper case letters are the variables that are used to create objects, in python its classes, here its implementations/structs/enums/etc….

Yeah, the difficulty in the beginning in a new language is to tell what’s what first :) New terminology, different way of thinking. Rust brings a lot of concepts to the table that I needed time to internalize.

unwrap has nothing specifically to do with join(). join() returns a Result (https://doc.rust-lang.org/std/result/), unwrap just gives you the value contained in the OK version of the result and panics on Err. There are many other ways to handle Result besides unwrap. The Result type forces you to handle possible errors, rather than just throwing them up the call stack like other languages.

You would probably not use unwrap() in production code because of the possibility for it to return an Err and crash. In production code you’d want to have a match case to handle the Err result, or use something like unwrap_or() to return another value if the Result is type Err.

Makes sense, thanks a lot. Pretty much what I’ve gathered during the month as well. Error handling is a pretty big part of the philosophy of any language, and usually it takes time to untangle. :)

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.