🦀

8 Rust Language Exercises

Peter Varo - Senior Backend Engineer

Tom Steavenson - Engineering Manager

At hx, our Engineering team has a Rust lecture series for engineers, encouraging our teams to learn and get familiar with the programming language. We ran 30 hour-long lectures, following the structure of The Rust Book, and included homework exercises. New joiners to hx have enjoyed going through the recordings and submitting their solutions to the exercises for review by their peers.

Exercises are often the thing that's missing from programming language learning resources, so we thought to contribute these to the world! In this blog, we'll take you through 8 exercises to test your Rust knowledge - let's get into it!

Warm-up Exercises

These are some small warm-up exercises that you can write as a single function. The Rust Playground is a good place to write these up in quickly.

  1. Write a function to return the nth Fibonacci number
  2. Write a function to convert between Celsius and Fahrenheit
  3. Write a function to generate the lyrics to “The Twelve Days Of Christmas”
For each exercise, have cargo installed, and create a new project. Each exercise will provide you with a main.rs to get you started.
Note: Some of the early exercises are designed around having only covered the book up to the accompanying chapter.

If you are more experienced in Rust, there may be easier ways to solve the problem with more advanced language knowledge.

We’ll denote which chapters of the book the exercises correspond to, and you can decide whether to try and solve the problems with only the parts of Rust that have been covered up to that point or bring your full Rust knowledge to bare.

Exercise #1 - Rectangular

This exercise is based on Chapter 5 of the Rust Book (Structures).

In this exercise, you will build a small utility that calculates the area and perimeter of a rectangle from the width and height provided by the user.

Your program will draw a rectangle with the correct ratio on the screen (where the minimum screen width is 80 and the minimum height is 40), place the width and height properties on the rectangle, and the calculated values inside it.

Note: You can get creative when it comes to drawing the rectangle but one good way of doing it is with the box-drawing characters: https://en.wikipedia.org/wiki/Box-drawing_character#Box_Drawing

To get started, replace the main.rs of your new project with the code below. You can copy this code from the Rust Playground using this link.

And add clap to your dependencies in cargo.toml:

clap = { version = "3.1", features = ["derive"] }

Example output

$ cargo run -- --width 22 --height 6
      width: 22
┌────────────────────┐
│                    │
│ AREA: 132          │ height: 6
│ PERIMETER: 56      │
│                    │
└────────────────────┘

Draw wide or tall rectangles

You could use other characters to represent edge-cases, e.g. "half" lines and dashed lines to indicate visual trimming:

┌──────╴╌╶──────┐
│               │
╵               ╵
╎               ╎
╷               ╷
│               │
└──────╴╌╶──────┘

Bonus

If you like a bit of an extra challenge and you wish to practice responsibility separation more, you could add any of the following extra lines to the definition of the Arguments struct. You can copy the code from the Rust Playground using this link.

#[derive(Parser)]
#[clap(author, version, about)]
struct Arguments {
    // ...
    #[clap(short, long, help = "Use ASCII only characters to draw the rectangle")]
    ascii_only: bool,
    #[clap(short, long, help = "Use thicker lines to draw the rectangle")]
    bold_lines: bool,
}

And make your program draw the rectangles with bold lines and/or ASCII only character via the --bold-lines and --ascii-only command line parameters.

Exercise #2 - Gringotts

This exercise is based on Chapter 6 of the Rust Book (Enums and Pattern Matching). For more on pattern matching, also read Chapter 18.

A small utility for Muggles to convert between the different coins of the wizarding currency of Great Britain.

To get started, replace the main.rs of your new project with the code below. You can copy the code from the Rust Playground using this link.

use clap::Parser;


#[derive(Parser)]
#[clap(author, version, about)]
struct Arguments {
    #[clap(help = "The amount of money given in <FROM> coin")]
    amount: f64,
    #[clap(short, long, help = "\
        Coin the <AMOUNT> is defined in \
        (Possible values: knut, sickle, and galleon)\
    ")]
    from: String,
    #[clap(short, long, help = "\
        Coin the <AMOUNT> should be converted to \
        (Possible values: knut, sickle, and galleon)\
    ")]
    to: String,
}


fn main() {
    let arguments = Arguments::parse();
    dbg!(arguments.amount, arguments.from, arguments.to);
}

And add clap to your dependencies in cargo.toml:

clap = { version = "3.1", features = ["derive"] }

Example Output

$ cargo run -- 12 --from sickle --to knut
12 Sickles → 348 Knuts

Exchange Rate

image

Bonus

If you like a bit of an extra challenge you could add any of the following extra lines to the definition of the Arguments struct:

#[derive(Parser)]
#[clap(author, version, about)]
struct Arguments {
    // ...
    #[clap(short, long, help = "\
        Coin the <AMOUNT> is defined in \
        (Possible values: knut, sickle, galleon, pound, and ...)\
    ")]
    from: String,
    #[clap(short, long, help = "\
        Coin the <AMOUNT> should be converted to \
        (Possible values: knut, sickle, galleon, pound, and ...)\
    ")]
    to: String,
    #[clap(short, long, help = "Print out the result without fancy formatting")]
    simple_output: bool,
}

For Muggles it might make more sense to convert from their currency to the wizard one, after all, that's what they are using on a daily basis. The addition to from and to is only in the help text: both should accept more currencies now, it's up to you how many currencies you wish to support. Have a look at the approximate exchange rate and cherry pick your favourites.

You can also use this gringotts utility in conjunction with other programs. For instance, you might want to pipe the output of this program to another one and in that scenario having the fancy, formatted output is just making things much more complicated for the subsequent user of the conversion result. That's where the --simple-output flag comes in e.g.

$ cargo run -- 12 -f sickle -t knut --simple-output
348

$ cargo run -- 12 -f sickle -t knut --simple-output | xargs printf "My sickles in knuts: %.2f\n"
My sickles in knuts: 348.00

Exercise #3 - Bye Bob

This exercise is based on Chapter 8 of the Rust Book (Common Collections).

A text interface that allows you to track and query employees within a company and its departments.

To get started, replace the main.rs of your new project with the code below. You can copy the code from the Rust Playground using this link.

use std::io::{self, Write};

fn main() {
    while true {
        let input = prompt_user();

        // Just echo back the input
        println!("{input}");
    }
}

/// Requests input from the user.
fn prompt_user() -> String {
    print!("> ");
    io::stdout().flush().expect("Error: Failed to flush stdout");

    let mut line = String::new();
    io::stdin()
        .read_line(&mut line)
        .expect("Error: Could not read from stdin");

    return line.trim().to_string();
}

Example Output

$ cargo run

Welcome to Bye Bob, the next generation HiBob CLI! Please enter your commands below:
====================================================================================

> Add Alice to Engineering
Added Alice to Engineering

> Add Amir to Engineering
Added Amir to Engineering

> Add Bob to Sales
Added Bob to Sales

> List
Engineering
------------
Alice
Amir

Sales
-----------
Bob

> Get Engineering
Alice
Amir

> Remove Amir from Engineering
Removed Amir from Engineering

> List Engineering
Alice

Commands

Your text interface should support 4 commands:

image

The exact form of these commands is up to you, the example output above is just a suggestion.

Bonus