The Rustlings exercises - part 1
Tags: rust, beginners, learning, practice
Published: 2021-06-13 15:54:50

To continue building my understanding of Rust, I searched for some simple Rust exercises. Hence, I dedicated my weekly personal work time to the Rustling exercises.

> Greetings and welcome to rustlings. This project contains small exercises to get you used to reading and writing Rust code. This includes reading and responding to compiler messages!
>
> -- [https://github.com/rust-lang/rustlings](https://github.com/rust-lang/rustlings)

I believe that this workshop is pretty neat. Thanks to everybody who contributed to it!

I'll split my notes into two posts, as Rustlings contain many (many!) exercises. Besides that, I need to learn about the more advanced themes such as threading.

In those two posts, I'll only describe the exciting bits. If you're interested, you can find the solutions themselves on [GitHub](https://github.com/ajavageek/rustlings/tree/work). I'd urge you to try by yourself, though.

## Conditionals

The [first exercise](https://github.com/ajavageek/rustlings/tree/work/exercises/if) is about conditionals. To return a `bool`, the following snippet doesn't compile:

```rust
if a > b {
  a
}
b
```

Instead, you have to be explicit about this:

```rust
if a > b {
  a
} else {
  b
}
```

## Move semantics

The real "fun" about data ownership starts here. Exercises in [this folder](https://github.com/ajavageek/rustlings/tree/work/exercises/move_semantics) require you to wrap your head around ownership and borrowing.

```rust
let vec0 = Vec::new();

let mut vec1 = fill_vec(vec0);          // 1
```

1. The `vec1` variable now owns the `vec0` parameter!

The idea is to pass a reference to `vec0` instead so that that `fill_vec()` only borrows it.

```rust
fn fill_vec(vec: &[i32]) -> Vec<i32> { // 1
    let mut vec = vec.to_vec();        // 2
```

1. Pass a slice
2. Create a new `Vec` from the slice and return it

`more_semantics3.rs` is much easier as the compiler outputs the correct hint to fix the issue.

## Structures

One of the main points in learning a new language is getting familiar with the available ways to design complex models. It's the theme of this [series of exercises](https://github.com/ajavageek/rustlings/tree/work/exercises/structs). In `structs1.rs`, there are two lessons:

1. Use `&str` instead of `String`.
2. When using references, we need to take care of the lifetime.

`struct2.rs` teaches about copying existing structures into new structures. Rust allows you to create a new `struct` from an existing one by defining only different field values and copying the same ones using `..`. You can read more about it [here](https://doc.rust-lang.org/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax).

## Collections

Another significant point is learning collections. This [series](https://github.com/ajavageek/rustlings/tree/work/exercises/collections) is on `Vec` and `HashMap`.

```rust
fn vec_loop(mut v: Vec<i32>) -> Vec<i32> {
    for i in v.iter_mut() {
        // TODO: Fill this up so that each element in the Vec `v` is
        // multiplied by 2.
    }

    // At this point, `v` should be equal to [4, 8, 12, 16, 20].
    v
}
```

The syntax already uses mutability, _i.e._, `mut` and `iter_mut()`, so that is not an issue. But `iter_mut()`, as well as `iter()`, return references. Hence, we have to dereference both the left *and* the right side of the assignment.

Regarding `HashMap`, it seems there's no available macro like `vec![]` for `Vec`.

## Error handling

After structures and collections, I think error handling completes the trinity of the foundations of a language. For example, Go put me off with its way.

In `errors.rs`, I got confused: I tried to create a custom `Err` type. But one only needs to replace `Some` with `Ok`, and use the standard `Result` type.

![Option and Result enums](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/htmhpcxz9hdoot9318ek.png)

I solved the `error2.rs` by using `match`. I forgot that each `match` clause must end with a comma.

```rust
match qty {
    Ok(i) => Ok(i * cost_per_item + processing_fee),
    Err(e) => Err(e),
}
```

It's not the way. It's much easier to use `map()` and the proper closure. There's no comma between the closure's parameter(s) and its body as opposed to' match'.

To me, the compiler hint was no help to solve `error_handlingn.rs`. I made the wrong assumption initially and tried to use `CreationError` as the return type. It didn't help that I didn't read [the book's section](https://doc.rust-lang.org/book/ch15-01-box.html) about the `Box` type.

## Generics

As opposed to Go, Rust provides [generics](https://doc.rust-lang.org/book/ch10-01-syntax.html).

For `generics3.rs`, I had to use bounds on generics. The syntax is similar to Java's. The difficulty lies in the fact that you must set the bound on both the trait *and* its implementation.

## Options

This [series](https://github.com/ajavageek/rustlings/tree/work/exercises/option) is pretty straightforward and deals with [the `Option` enum](https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html?highlight=option#the-option-enum-and-its-advantages-over-null-values).

For `option2.rs`, I had to re-read the `if let` [syntax](https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html). It's an assignment, so it accepts only a single `=` sign.

## Results

`result1.rs` made me realize that `match` only matches on values, not on expressions. To check for expressions, use `if else`.

## Tests

Exercises on tests are great. It's a good occasion to check which base assertions are available:

1. `assert()`
2. `assert_eq()`
3. `assert_ne()`

## Iterators

The [final series](https://github.com/ajavageek/rustlings/tree/work/exercises/standard_library_types) for today's post of exercises is on iterators.

In `iterator2.rs`, I learned that a `char.to_uppercase()` doesn't return a `String` but a dedicated `ToUpperCase` type. The reason is that some languages don't have a simple mapping from lower case to upper case, _e.g._, German `ß`.

TIL: `join("")` function when you need to collect additional items in between. The associated type is `std::slice::Join`. I didn't find the association between `Vec<>` and `join()`...

I started `iterator3.rs` with `list_of_results()` because it's (much) easier. `division_results` is of type `Map<IntoIter<i32>, fn(i32) -> ?>`. `x` must be of type `<Vec<Result<i32, DivisionError>>>`. It means we only need to `collect()` the `Iterator`: it will trigger the `map()` closure. Done.

`result_with_list()` requires a `<Result<Vec<i32>, DivisionError>>`. I had to go through the [documentation](https://doc.rust-lang.org/rust-by-example/error/iter_result.html#fail-the-entire-operation-with-collect) to find the `collect()` function that applies explicitly to an iterator of `Result`. The idea is to collect first like in the previous function, then make it back to an iterator of `Result`, and finally `collect() again.

As mentioned in the documentation, you need to help the compiler with `collect()` because, in general, it cannot infer types correctly:

> Because `collect()` is so general, it can cause problems with type inference. As such, `collect()` is one of the few times you'll see the syntax affectionately known as the 'turbofish': `::<>`. This helps the inference algorithm understand specifically which collection you're trying to collect into.
>
> -- [pub fn collect(self) -> B](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect)

Solving `iterators4.rs` is easy with recursion. The compiler hints about ranges: it's actually relatively easy combining them and the `fold()` function. The latter is fairly common in Functional Programming.

This is it. In the next post, I'll provide the notes I took while solving the remaining exercises.

The complete source code for this post can be found on Github on the `work` branch:

{% github ajavageek/rustlings/ %}

**To go further:**

* [Rustlings](https://github.com/rust-lang/rustlings)

_Originally published at [A Java Geek](https://blog.frankel.ch/start-rust/3/) on June 13<sup>th</sup> 2021_