My Rust Journey - 18 Oct 2024

This is the 6th post about my journey learning the Rust programming language using the Rust Book. Previous posts include:

Chapter 1: Basics of Rust and Cargo

Chapter 3: Mutability and shadowing, variables and constants, scalar and compound data types, functions, control flow with conditional statements and loops

Chapter 4: Ownership, reference and borrowing, and slice type

Chapter 5: Ownership, reference and borrowing, and slice type

Chapter 6: Enums, Control Flow and Matching

I am documenting this as I think it is a useful thing to do for people interested in learning Rust from my non-developer perspective.

At this stage, you have already installed Rust on your machine, and you are ready to write and run your first Rust programs.

I am using VS code with the rust-analyzer extension. I am working on an M1-mac.

The following tutorial will cover Chapter 7 of the Rust Book. It is meant to be a summary and used with the book as a complementary source of information.

Rust module system includes: Packages: cargo features to build, test, and share crates Crates: a tree of modules that produces a library or executable Modules and use: to control the organization, scope, and privacy of paths Paths: a way of naming items such as structs, functions, and modules

Packages and Crates

A crate is the smallest amount of code the Rust compiler can consider at a time.

A crate can be binary or library. Binary crates are programs containing a main function, and they can compile to executable files. Library crates do not have a main function and do not compile, but they share functionalities to be shared with multiple projects.

A package is a bundle of one or more crates providing a set of functionalities. A package contains a cargo.toml file describing how to build the crates. A package can contain multiple binary crates and, at most, one library crate.

When you create a new package with cargo new, the toml file uses Rust convention that src/main.rs is the crate root of a binary crate with the same name as the package, and src/lib is its crate root.

Modules to Control Scope and Privacy

Modules let us organize code within a crate. The code within a module is private by default.

Below is an example code of a module tree structure within the src/lib.rs crate.

mod front_of_house {
    mod hosting {
        fn add_to_waitlist() {}

        fn seat_at_table() {}
    }

    mod serving {
        fn take_order() {}

        fn serve_order() {}

        fn take_payment() {}
    }
}

The hosting and serving are sibling modules with the parent module front_of_the_house.

Paths

Paths are used to find items within a module tree. A path can be:

Absolute: full path starting from the crate root, for code on an external crate Relative: starts from the current module and uses self, super, or an identifier in the current module

Modules are by default private; to make them public, we need to specify them with pub. See below:

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}

        fn seat_at_table() {}
    }

    mod serving {
        fn take_order() {}

        fn serve_order() {}

        fn take_payment() {}
    }
}

pub fn eat_at_restaurant() {
    crate::front_of_house::hosting::add_to_waitlist(); //absolute path

    front_of_house::hosting::add_to_waitlist(); //relative path
}

First, we need to make hosting and add_to_waitlist public so that we can use them within the function eat_at_restaurant.

Relative Paths with super

The super syntax is equivalent to ../ in the file system and refers to an item in the parent module.

Making structs and enums public

If you make a struct public, its fields will be private by default unless you make them public. Instead, if you make an enum public, all enum variants will also be public.

Bringing paths into scope with the “use” keyword

use crate::front_of_house::hosting will create a shortcut for future use so that you can use:

hosting::add_to_waitlist()

instead of

crate::front_of_house::hosting::add_to_waitlist()

The way the path is used is called “idiomatic” in that we are still specifying the module where the function is defined. The unidiomatic way would be determining the path as follows:

use crate::front_of_house::hosting::add_to_waitlist;

And then call the function add_to_waitlist. This creates confusion about where the function is coming from, especially if you have large pieces of code.

You can also provide new names with the as keyword when you specify a path: use std::io::Result as IoResult.

With pub use you can make available a name for any code to use from a new scope.

You can also use nested pahs: use std::{cmp::Ordering, io} is equivalent to:

use std::cmp::Ordering;
use std::io;

and use std::io::{self, Write} is equivalent to:

use std::io;
use std::io::Write;

use std::collections::* brings into scope all public items defined in a path.

You can also separate modules into different files, as explained here.

In this chapter, we learned about managing your project with packages, crates, and modules in Rust. See you in the next post!

0
filippoweb3Post author

🔥 Web3 explained from the non-developer's POV. 🚀 Helping Polkadot users explore the ecosystem with confidence. I post daily on socials. Opinions are mine.

2 comments

Loading replies...

🔥 Web3 explained from the non-developer's POV. 🚀 Helping Polkadot users explore the ecosystem with confidence. I post... Show More