In this episode I aim to learn some Rust concepts beyond Hello World, using the book "The Rust Programming Language" as a guide.

I'm doing a series of 25 minute sessions where I try to get familiar with the Rust programming language. The blogposts in these series are the notes I took of the lessons learned along the way.

Warning to the reader: As always in this series, these are notes I take as I'm learning. They reflect my current understanding and may be incorrect!

Rust Learning Resources

For my first introduction I went to the book The Rust Programming Language.

The second edition of this book, completely rewritten, is available online (and soon in print).

Guessing Game Tutorial

From the book: I will follow this and take notes of interesting things.

https://doc.rust-lang.org/book/second-edition/ch02-00-guessing-game-tutorial.html

Creating a new Project

You make an empty project using cargo:

cargo new guessing_game --bin

It's a git repo by default.

"guessing_game" is the name of the project.

Associated Function new()

Instantiating an object from a class looks like this:

let mut guess = String::new();

To me, it looks like this line is "newing up a String": it creates a new instance of the string.

We learn in the book that the new method is a method on the class level. Which in some languages (like C#) is called a static method. In Rust it's called an associated function.

It seems the new method isn't present in every class. And it could also be called something else.

So using new in this case is a convention.

Mutability

mut is optional and not there by default. This means a "variable" can be assigned a value only once. You can not change the value afterwards.

In the guessing game, the variable must be mutable because it gets modified by io::stdin().read_line later.

Error changing an immutable variable

If the string isn't made mutable using the mut keyword, the following compile-time error occurs when passing it to a function expecting a mutable variable:

error[E0596]: cannot borrow immutable local variable `guess` as mutable
  --> src\main.rs:10:32
   |
8  |     let guess = String::new();
   |         ----- consider changing this to `mut guess`
9  |
10 |     io::stdin().read_line(&mut guess)
   |                                ^^^^^ cannot borrow mutably

It talks about "borrowing". I've heard about scope and ownership in Rust and expect to learn more soon.

If I remove the mut in the call to read_line itself, I get the following error:

error[E0308]: mismatched types
  --> src\main.rs:10:27
   |
10 |     io::stdin().read_line(guess)
   |                           ^^^^^
   |                           |
   |                           expected mutable reference, found struct `std::string::String`
   |                           help: consider mutably borrowing here: `&mut guess`
   |
   = note: expected type `&mut std::string::String`
              found type `std::string::String`

From this, we learn "&mut" means doing "mutably borrowing".

If I use io::stdin().read_line(&guess) the following error appears:

error[E0308]: mismatched types
  --> src\main.rs:10:27
   |
10 |     io::stdin().read_line(&guess)
   |                           ^^^^^^ types differ in mutability
   |
   = note: expected type `&mut std::string::String`
              found type `&std::string::String`

Interestingly, the mut on the variable declaration is not enough for Rust to know the references is mutable. If I remove mut in the variable declaration, the above error is still the same. In other words: even if your variable is locally declared mutable, a reference to it isn't necessarily mutable.

Macros

Apparently, println! is not a function but a macro. Which means, I think, the compiler translates it to some other code before compilation.

Includes

On the top of the file we use use std::io; to import the io methods of the standard library.

Interestingly, when calling the methods from this library, we can do it like this: io::stdin().read_line. I'm not sure why the io:: needs to be repeated. Coming from C# I'm used to the following:

using System;

...
  Console.WriteLine("..."); // With 'using System;' is present
  System.Console.WriteLine("..."); // Without 'using System;'

Instead of io::stdin and a use on top, it was also possible to write std::io::stdin().read_line. Much like the System.Console.WriteLine("..."); line in the above C# example.

What if I just to use std?

error[E0254]: the name `std` is defined multiple times
 --> src\main.rs:1:5
  |
1 | use std;
  |     ^^^ `std` reimported here
  |
  = note: `std` must be defined only once in the type namespace of this module
help: You can use `as` to change the binding name of the import
  |
1 | use std as other_std;
  |     ^^^^^^^^^^^^^^^^

Conclusion

That was the 25 minutes today. I explored a few things in the example code from the book. I haven't made anything useful yet, but I got introduced to a couple of concepts:

  • Mutability
  • What Mutably Borrowing looks like (a reference declared with &mut)
  • Macros exist
  • What "includes" look like
  • That a static method is called an associated method
  • That classes declare an associated New method, but are not forced to do this (at least that is what I understood)

Can't wait to get the game working in the next 25 minutes.