I’m learning Rust and this is the first post about my experience with it. I’m a Ruby programmer, so you may wanna check out my post about the decision of learning Rust for more background.
I delayed this post a lot because I didn’t do a lot of Rust in my first month. I had several things going on in my life, so I couldn’t focus on Rust as much as I would like. Anyway, I know that I have to put on paper some of my impressions before I forget them, so here I am.
🔧 Setting up
Setting up Rust was very easy: rustup
makes everything pretty straightforward, so I had no problems here. Cargo
is also very good, and I had no problems installing libraries (crates) too! Well done, Rust!
📚 Reading
As I didn’t have much time to actually code Rust, I spent a lot of time reading about it. Regarding this, Rust is the language with best learning tools I’ve seen to this day! Not only it has a book (The Book, I must say 😅) that guides you in the first steps to know the language and its unique features, but Rust also has a Learn by Example book!
I think that this is a very nice pairing and both books are awesome in their way:
- Wanna start from scratch and learn about a concept? Check out The Book.
- Just want to know the syntax for creating an anonymous function? Here’s a book full of examples.
Chapter 04 (the one about Ownership) of The Book was especially great for me. My mind was blown after reading it. Not because it was hard to read, but because everything made SO MUCH sense! All the pain points in C++ seemed to be solved with this ✨magical✨ compiler. Well, that was in theory, let’s see how I did in practice, tho.
✍️ Writing
I know that I should pick a simple topic to get me started when learning a new language, but I can’t control my desire to create things. I don’t like to create build-and-throw-away things, either. They should be useful things (at least to me)! So, to learn Rust I started building an interpreter following Lisperator.net’s tutorial.
To be honest, my first 100 lines of Rust were the basic algorithms in Chapters 01 and 02 of The Book, just to test out my setup and get to know a bit of the syntax. Four-space tabs and semicolons everywhere were kinda weird for me, but at least Rust doesn’t require explicit returns (and can avoid semicolons in the last expression too). rustfmt
was a neat addition, since I don’t have to care about how my code should be indented. 👍 for Rust here too!
I was looking for things that were different in Rust and Ruby. Here are some examples of what I found:
🔢 Underscores and numbers
Rust, like Ruby, allows underscores to separate numbers. The difference is that Rust allows multiple underscores (you can even have trailing ones):
// Both examples work like a charm in Rust
let a = 1_;
let b = 1_____2;
// SyntaxError in Ruby: trailing `_' in number
a = 1_
// SyntaxError too
b = 1_____2
❓ Rust has no ternary operator
We can use if/else
expressions — which I’m very familiar with, coming from Ruby —, so I didn’t care not having it in Rust.
📏 Ranges are not inclusive
To me this was weird because it’s not the same I’m used to in Ruby.
- Ranges in Rust:
println!("{}", (1..10).count()) // => 9
- Ranges in Ruby:
puts (1..10).count # => 10
I guess I’ll do some off-by-one errors until getting used to this. I found out later that there’s an inclusive version of ranges too:
println!("{}", (1..=10).count()) // => 10
All in all, it was not that different from Ruby. Yeah, Rust is a “curly braces language”, but with things like enums
and impl
, I could translate my OOP code fairly well. The Gentle Introduction To Rust chapter on OOP was particularly helpful for that.
The main difference from Ruby for me was that in Rust I feel like I have to think about memory usage in each line of code I write.
❌ Learning by error
Generally, Rust gave me really good compiler error messages. The fact I can run rustc --explain SOME_ERROR
and see details for this error makes learning Rust (without leaving the terminal) much easier.
The stdlib documentation seems thorough and very modern: it has links for return types, traits, enums, tips, and examples.
🤝 The compiler is my friend
After a long time writing in an interpreted dynamic typed language like Ruby, Rust’s compiler was a breath of fresh air. It’s so good to know that I got a type checker having my back when I screw things up. I feel like I have to write fewer tests but still have the feeling that everything is working.
Another difference is the lack of null
values 🙌. This is one of my favorite choices in Rust comparing to C++. Option types are much better IMO and having pattern matching makes dealing with them quite easy. About that, some constructors were really well thought out, like: enums
, if let
,while let
. They are cohesive and fit the language like a glove.
🦀 Rusty Code
It’s funny to learn a new language because I can’t tell what is idiomatic or not yet. For example, while coding my interpreter, I created the following function:
fn is_punctuation(c: &char) -> bool {
",;(){}[]".contains(c.clone())
}
I used clone
to satisfy the compiler, but I had the feeling that it was unnecessary. Later I ended up refactoring it to this version (which I think is better):
fn is_punctuation(c: &char) -> bool {
",;(){}[]".contains(*c)
}
I don’t know if having the string inside the function is a good idea or if I should extract it into a constant or something. And that kind of question happened a lot:
🤔 A lot of questions
As a beginner, I don’t know what are the best practices and idioms in Rust, so I had a lot of questions to ask. I had some classic beginner stuff like how to work with modules
and the String
vs str
vs &str
dilemma (which this blog post and my rustacean friend@PotHix helped me to understand), and I’m expecting to have even more questions about memory management and that kind of stuff.
I also don’t know when to move and when to borrow values, so I’m defaulting to borrow all the things and cloning defensively.
I just found out 📎 Clippy, which I think is kinda like👮♂️ Rubocop for Rust, so I hope it will help me to write better Rust code.
🔮 Future
I plan to keep writing my interpreter in Rust to learn more about this language. One thing I didn’t explore yet is testing. Since I’m a big fan of TDD I hope Rust has a good test support. I have an interest in digging a bit in game development with the Bevy game engine.
I’ll also make a post comparing Rust with Crystal showing my experience with both languages.
Oh, and if you’re a rustacean, feel free to give me any advice or tip you think is useful! Bye 🦀!
Top comments (5)
Matheus, thanks so much for taking the time to write these articles! I'm learning a lot from them as I'm also finally getting my hands dirty with Rust.
I come from a mostly PHP, and Angular, TypeScript, JS background, so there are quite a few new concepts I need to learn and understand. I wish I started with something like C/C++ back in the day, just to be comfortable with memory management/pointers etc. haha! But all in good time, I guess.
Looking forward to your updates
Thank you Andries for this comment, it really means a lot to me! I'm glad those articles were helpful to you!
I'm writing an article per month, so stay tuned! Cheers!
I don't know your background, but I wanted to share a couple of thoughts..
1) "semicolons everywhere were kinda weird for me, but at least Rust doesn’t require explicit returns (and can avoid semicolons in the last expression too)."
Have you ever learnt Pascal? It also (at least, when I learnt it in 1983) had this. There's differentiation between function and procedures. Both return at the end (i.e. no jumping out in the middle, which was frowned upon at the time as doing so isn't much different to 'goto'), but the return value from a function is set by assigning a value to the function name. Interesting, worth a look! Also semi-colons separate statements, rather than terminate them, so not needed if the next bit of code (end of a subroutine for example) doesn't count as a statement.
2) "Ranges are not inclusive"
I don't know Ruby, but your comment made me look, as ranges are inclusive in Ada (released to the public in 1983), and Ruby has adopted similar 's..e' syntax. However you didn't mention Ruby's 's...e' syntax, which gives you a range that doesn't include the end index (alternatively the ::new(s, e, true) version, where the last parameter is 'exclude_end'). It seems like, essentially, Rust and Ruby have just chosen the defaults to be opposite.
3) "Underscores and numbers"
Yes, Ada (1983) has this. Also various number bases can be easily specified. For example:
16#ABCD_EF01# - - hexadecimal 32-bit integral value.
2#1010_1011_1100_1101_1110_1111_0000_0001# - - same in binary
3#0.1# - - base 3 floating point, => 1/3rd
4) "Rust has no ternary operator"
Neither had Ada originally, but "if expressions" and "case expressions", which are similar to the ternary operator, were added in 2012. It looks like they're also similar to Ruby's if/else expressions, so....
5) "The compiler is my friend"
This is what Ada programmers have known since the early '80s :-)
Thanks for the article. I've had a cursory glance at Rust, but never got very far since a lot of the 'simple' stuff appears to be a reinvention of the wheel, with different syntax for no apparent benefit. Maybe I should try harder :-)
Thanks for your time writing this. Yeah, much of the things were quite similar between Ruby and Rust. Also, Ada was one of the languages that influenced Ruby, according to Matz.
Rust has TDD built into it. Try
cargo new myproj --lib
and thencargo test
to see it in action.