Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

My Rust Playground

by Ilario A. Azzollini

As a researcher and an engineer — not a “pure software developer” (at least not by birth) — I see programming languages as tools. In my view, a language is always a means to an end.

Because I work close to the development side of things, I care deeply about developer experience. Rust offers a powerful and well-designed ecosystem. The tools are reliable and pleasant to use. They make it easier to write high-quality code and focus on the problem instead of fighting the language.

This repository is my Rust playground: a place where I collect what I study, experiment with new ideas, and explore concepts I want to understand more deeply.

Why Rust

This page is work in progress… Atm, it only contains some useful references

Why should you at least consider Rust? And why not?

Already watched and suggested for other interested people (in no specific order):

To be properly watched:

References

  • The Rust Programming Language Book: The main introductory book about Rust, usually referred to as “the book” by rustaceans
  • The rustc book: The compiler for the Rust programming language, provided by the project itself. It also comes with its own set of lints.
  • Clippy Documentation: A collection of lints to catch common mistakes and improve your Rust code. It is a superset of rustc lints.
  • Rust by Example: a collection of runnable examples that illustrate various Rust concepts and standard libraries. Usually consumed alongside “the book”, gives a more interactive experience to learning Rust basics
  • The Rust Reference: a book that serves as a reference to what is available in stable Rust.
  • The Rust Standard Library Documentation
  • crates.io: The Rust community’s crate registry
  • The Cargo book: cargo is the Rust package manager and build system
  • The rustdoc Book: The standard Rust distribution ships with a tool called rustdoc. Its job is to generate documentation for Rust projects.
  • Rust API Guidelines: This is a set of recommendations on how to design and present APIs for the Rust programming language. They are authored largely by the Rust library team, based on experiences building the Rust standard library and other crates in the Rust ecosystem
  • Command Line Applications in Rust: Rust is a statically compiled, fast language with great tooling and a rapidly growing ecosystem. That makes it a great fit for writing command line applications: They should be small, portable, and quick to run. Command line applications are also a great way to get started with learning Rust; or to introduce Rust to your team!

Project Setup

This section provides a step-by-step guide to setting up the development environment required to follow along with the examples in this book. The setup relies on widely used, free tools that work seamlessly on Windows, Linux, and macOS.

Prerequisites

The following tools are required:

  • Git
  • Docker
  • VS Code (optional, but recommended for a fully integrated development environment)

After installing these tools (and ensuring they are available in your system PATH), open a terminal or PowerShell and run the following commands:

git --version
docker --version
code --version

If the installation was successful, each command should print the installed version without errors.

Development Environment Setup

As mentioned earlier, we will rely on Docker to containerize our development environment. This approach ensures that everyone works in the same reproducible environment, regardless of their host operating system.

The machine on which Docker is installed — and from which we build Docker images and run Docker containers — will be referred to as the host PC. Regardless of the host PC’s operating system, all development work in this book will take place inside an Ubuntu Docker container.

1. Clone the repository

To get started, open a terminal and navigate to the directory where you want to clone this repository, then run:

git clone https://github.com/ilarioazzollini/rust_playground.git
cd rust_playground

From this point on, all terminal commands in this guide should be executed from the root directory of this repository unless otherwise specified.

2. Build the Docker container image

Now, we can build a new Docker container image by running:

docker build -f docker/Dockerfile -t rust_playground:latest .

If the build completes successfully, the image should appear in the list of available images:

docker image ls

If you want more details about this image, you can refer to the file rust_playground/docker/Dockerfile

3. Run a Docker container

Next, run a container from the image we just built:

docker run -it --rm --privileged --network=host -v .:/root/rust_playground -w /root/rust_playground --name rust-dev-cont rust_playground:latest bash

You should now be inside a terminal session running in the Docker container.

If you want to check the container’s status, open another terminal on the host PC and run:

docker container ls

You should see a running container named rust-dev-cont.

Now return to the terminal inside the container. If everything worked correctly, your current working directory should be /root/rust_playground. You can verify this by running:

pwd

You should also see the contents of the rust_playground repository by running:

ls

This works because the repository directory on the host PC is mounted inside the container using the volume flag -v .:/root/rust_playground. This means the folder is shared between the host and the container.

We can also verify that the Rust toolchain is correctly installed inside the container:

cargo --version
rustc --version

When you are done, you can exit the container by running:

exit

At this point, the development environment is fully functional and we can start writing and running Rust code.

4. VS Code with Dev Containers

A fully integrated development setup can be achieved using VS Code and the Dev Containers extension.

First of all, we need to open a terminal and install (or update) the required VS Code extensions:

code --force --install-extension ms-azuretools.vscode-containers
code --force --install-extension ms-vscode-remote.remote-containers

Then, navigate to the root directory of the rust_playground repository:

cd <rust_playground>

And open the project in VS Code:

code .

Open the Command Palette (Ctrl+Shift+P, or Cmd+Shift+P on Mac OS), type

Dev Containers: Reopen in Container

and select it.

VS Code will close the current window, start a rust_playground:latest Docker container, and reopen the project inside the container. During this process, VS Code will also install the extensions defined in the file rust_playground/.devcontainer/devcontainer.json.

This setup provides useful development features such as:

  • IntelliSense
  • inline error reporting
  • automatic formatting
  • integrated debugging tools

Any terminal opened from this VS Code instance will automatically run inside the container.

If you modify the Docker image and rebuild it, remember to also rebuild the Dev Container. In that case, use Dev Containers: Rebuild Without Cache and Reopen in Container from the Command Palette.

Build the Project

Prerequisite: Regardless of whether you are using VS Code with Dev Containers or running docker run manually, from this section onward we assume that you are working inside a terminal session running in the Docker container we previously setup in Development Environment Setup.

To manage projects of arbitrary size, this repository is organized as a Cargo workspace. A workspace makes it easier to develop and maintain multiple related crates within a single project. This approach allows multiple binaries, libraries, examples, and experiments to coexist in a single repository while sharing dependencies and build configuration.

In this repository, the rust_playground/rs-ws directory acts as the workspace root and contains all the crates that we will develop throughout this book.

Build

To check all crates in the workspace, run:

cargo check --workspace --all-targets

Or, to directly build all crates in the workspace, run:

cargo build --workspace --all-targets

cargo check checks a local package and all of its dependencies for errors. This will essentially compile the packages without performing the final step of code generation, which is faster than running cargo build.

References:

Format

To format all crates in the workspace, run:

cargo fmt --verbose --all

References:

Run lints (Clippy)

Clippy is a collection of lints to catch common mistakes and improve our Rust code. It is a superset of the default rustc lints.

cargo clippy --verbose --all-targets --release

References:

Run tests

To execute all unit tests and integration tests across the workspace, run:

cargo test --workspace --all-targets

References:

Line coverage report

To generate a code coverage report, run:

cargo llvm-cov --workspace --lib --all-features

References:

Generate the docs

Rust can automatically generate documentation from comments written in the source code.

To generate the documentation for the entire workspace, run:

cargo doc --workspace --no-deps

References:

Basics

The goal of this section is to get familiar with Rust language basics, as well as Rust basic development tools.

We will start with the creation of our first binary crate, followed by the creation of our first library crate, and then we will focus on Rust syntax by developing some basic applications.

Hello, Binary Crate!

Prerequisite: As always, we assume that we are working inside a terminal session running in our usual Docker container (as previously explained in Development Environment Setup).

Source code at rust_playground/rs-ws/hello_world

Here we are, ready to say our “Hello, World!” in Rust. In order to do so, we will create our first binary crate. A binary crate produces an executable program.

We can create our new binary crate inside our Cargo workspace by running the cargo new command:

cd rs-ws
cargo new hello_world --bin

resulting in the creation of a crate having only one source file rust_playground/rs-ws/hello_world/src/main.rs:

fn main() {
    println!("Hello, world!");
}

Then, we can cargo fmt the crate by:

cargo fmt --verbose --package hello_world

We can cargo clippy the crate by:

cargo clippy --verbose --package hello_world

We can cargo check the crate by:

cargo check --package hello_world --release

Or cargo build:

cargo build --package hello_world --release

And finally, cargo run:

cargo run --package hello_world --release

Notice that if we omit the --release flag, the target will be checked/built/run with the default unoptimized + debuginfo profile

Hello, Library Crate!

Prerequisite: As always, we assume that we are working inside a terminal session running in our usual Docker container (as previously explained in Development Environment Setup).

Source code at `rust_playground/rs-ws/my_lib

A library crate provides reusable functionality that can be shared across multiple projects or binaries.

To create a new library crate, we can run:

cd rs-ws
cargo new my_lib --lib

resulting in the creation of a crate having only one source file rs-ws/my_lib/src/lib.rs:

#![allow(unused)]
fn main() {
pub fn add(left: u64, right: u64) -> u64 {
    left + right
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let result = add(2, 2);
        assert_eq!(result, 4);
    }
}
}

As we can see, by default the library crate has an add function, as well as an it_works test function, which is a unit test for add.

As for a binary crate, we can format:

cargo fmt --verbose --package my_lib

and we can run Clippy lints:

cargo clippy --verbose --package my_lib --release

and we can check the crate:

cargo check --package my_lib --release

or we can build it:

cargo build --package my_lib --release

In addition, we can run the tests:

cargo test --package my_lib --release

Get a line coverage report:

cargo llvm-cov --package my_lib --release

And, last but not least, we can automatically generate the documentation:

cargo doc --package my_lib --no-deps --release

that will end up in rust_playground/target/doc/my_lib, and we can serve the docs by:

python3 -m http.server 8080 -d /root/rust_playground/target/doc

and read them by opening a browser and navigating to http://localhost:8080/my_lib/index.html.

Create a binary crate depending on this library

Source code at `rust_playground/rs-ws/my_app

Now, we can create a new binary crate that depends on the library crate we just created. It is really easy to do so in a workspace.

Let us start by creating my_app:

cd rs-ws
cargo new my_app --bin

Then, navigate to its Cargo.toml file (at [rust_playground/rs-ws/my_app/Cargo.toml](https://github.com/ilarioazzollini/rust_playground/tree/main/rs-ws/my_app/Cargo.toml)), and under the dependenciessection, add a dependency onmy_lib` as follows:

[dependencies]
my_lib = { path = "../my_lib" }

Now, we can modify the `rust_playground/rs-ws/my_app/src/main.rs file as follows:

fn main() {
    let number_1: u64 = 2;
    let number_2: u64 = 1;

    let result: u64 = my_lib::add(number_1, number_2);
    println!(
        "Added numbers '{}' and '{}', and got the result: '{}'",
        number_1, number_2, result
    );
}

and we should be able to compile it and run it:

cargo run --package my_app --release

Useful References

There are many important aspects to consider while developing a library crate. Some useful references are:

Guessing Game

Prerequisite: As always, we assume that we are working inside a terminal session running in our usual Docker container (as previously explained in Development Environment Setup).

Source code at `rust_playground/rs-ws/guessing_game

Some parts of the program, as well as some parts of this doc page, are taken from The Rust Book.

Setup

First of all, we create a new binary crate:

cd rs-ws
cargo new guessing_game --bin

You can find the code in the source file rust_playground/rs-ws/guessing_game/src/main.rs.

This app will need a dependency on the rand crate. In order to add the dependency, we can either manually modify the rust_playground/rs-ws/guessing_game/Cargo.toml file or use cargo add (and maybe update them after a while by means of cargo update)

In this case, let us follow the Rust book and directly modify the toml file:

[package]
name = "guessing_game"
version = "0.1.0"
edition = "2024"

[dependencies]
rand = "0.8.5"

and then, the next time we are going to build the project, Cargo will automatically take care of it.

Build and Run

We can build and run the guessing_game package by:

cargo run --package guessing_game --release

And we can play the guessing game, which will look like this:

-----------------------------
Welcome to the Guessing Game!

Rules:
- I have generated a random integer number within the range of 0 to 100 inclusive
- You have 10 attempts to guess the number
- For each attempt, I'll tell you whether your guess is lower, higher, or the exact number
-----------------------------

Attempt #1. Please input your guess and press <Enter>.
50
LOWER!

Attempt #2. Please input your guess and press <Enter>.
80
LOWER!

Attempt #3. Please input your guess and press <Enter>.
90
YOU WON!

-------------------
End of the program.
-------------------