30 Day of Rust - Day 13
Tags: rust, learning, beginners
Published: 2023-05-07 20:37:02


**Hello world**,
I'm back from my day off yesterday (recovering from our company's **Spring Party**). I've had a think about what I've learned so far and talked to some other Rust learners about what to do next. Over the last couple of days I've been writing about my work on a CLI tool. I feel that my learning has slowed down and that I've bitten off too much in one go. Most of the time I found myself battling with the compiler to get simple got to work, and I found understanding documentation of APIs very challenging. Additionally, the kind of debugging I was doing didn't really help me draw useful conceptual conclusions which I could mention here on my blog.

Learning needs to be enjoyable and I want my blog to be a space to help me and you, dear reader, learn stuff about Rust. With both these things in mind, I've dropped working on that project for now. Instead I've started working my way through the [Rust book](https://doc.rust-lang.org/book/title-page.html). 

## Yesterday's questions answered
- To quote from the [Rust Book](The `derive` attribute generates code that will implement a trait with its own default implementation on the type you’ve annotated with the `derive` syntax.): *The `derive` attribute generates code that will implement a trait with its own default implementation on the type you’ve annotated with the `derive` syntax*. This is different from class inheritence.
- Returning an implementation type simply means to return a type that implements a given trait. This is a way to make functions generic and to decouple them from concrete implementations in your code.
- A single quotation `'` before a function argument indicates its lifetime to the Rust compiler. The reason why this is only sometimes required is because the Rust compiler can derive the lifetime in many cases, as it has a set of rules to for an input's and output's lifetime. You as the developer only need to step in when these rules are sufficient for the compiler. There is an entire [subchapter](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html?highlight=lifetime#lifetime-elision) in Rust book on this topic.
- `Send` and `Sync` are related traits. `Send` describes a value of a type whose ownership can be passed between threads. Most primitives are `Send` and the only notable type that is non-`Send` is the reference counter type `Rc`. `Sync` describes a value of a type that can be referenced across multiple threads. References to values that are `Send` yield a value which is `Sync`. Manually implementing these traits results in `unsafe` Rust. 

## Today's open questions
As I really just went over some basics in the Rust book today, I don't have any burning questions. Some general questions I'll look at tomorrow:
- How do you indicate to `cargo` what your project documentation is?
- What are some Python projects using Rust other than `Pydantic` and `Polars`?


## Back to basics
Going through the introduction of the Rust book I learned a coupe of cool little features. First of all, once you've installed Rust, you can access the Rust Book at any time by running the following command:
```shell
rustup docs --book
```
This could be useful for dedicated students who want to access their learning materials where there is no reliable wifi (e.g. when travelling on *Deutsche Bahn*).

Now the Rust book is pretty useful, but it's not going to help you understand external crates out of the box. But don't fear, Rust's package manager cargo is there to help. To get all the docs of the dependencies of your project as a file displayed in your browser, run:
```shell
cargo docs --open
```

## Cargo cares about project structure
Whenever you initiate a project using `cargo new`, it's important to note that `cargo` expects your code to be in a `src` folder. Also `cargo run` will always searching main.rs first, when the program is designed to yield an executable. Both of these facts demonstrate how Rust pushes developers to integrate best practices when structuring their code.

By default, cargo will also build a debug version of your executable. If you want an optimised release version, you can use the `--release` option.

Cargo uses the `Cargo.lock` file to guarantee reproducible builds. This means that developers should check the lock file into version control. Also, the `Cargo.toml` syntax implicitly using the  `^` symbol, meaning that cargo will look for the highest minor version update when installing the package. This behaviour is the same when running `cargo update`.

## Combining ordering with match
The standard library's `Ordering` enum provides a very readable interface for comparing values in a match statement, consider:
```rust
use std::Ordering;
const REQUIRED_RESPONSE_NUMBER: usize = 10;
let responses = get_responses();
match responses.len().cmp(&REQUIRED_RESPONSE_NUMBER) {
	Ordering::Greater => println!("Too many responses received"),
	Ordering::Less => println!("Too few responses received"),
	Ordering::Equal => println!("Correct number of responses received."),
};
```

The `Ordering` enum reduces the amount of code duplication that may often be required when comparing multiple cases. Not only do we avoid having to use a variable representing the case in each arm, but we also can skip on repeating the value we want to compare *against*. 

## Matching in loops
Another convenient way to use `match` is in the control flow of a `loop`.
```rust
user_inputs: [&str] = get_inputs();
for user_input in user_inputs.iter() {
	match is_valid_input(user_input) {
		Ok(_) => break,
		Err(__) => continue,
	}
}
```

Now here we don't really do anything too realistic when we get a valid a valid input. But the negative case could occur in a number of scenarios in a program. And really the learning here is really to try and think of Rust programs in terms of cases and outcomes. Dynamically typed languages like Python don't force this upon. And simple applications may get away with not handling edge cases or errors. But Rust encourages (and to some extend forces) you to think in these terms. Of course, the compiler helps you out a bunch, but it still takes time and energy to think through many of the possible execution paths of your program every time you write code.