As we have more and more pits, bigger and bigger, we have to manage the pits. Rust provides a pit management system that allows you to find, manage and fill your own pits in an orderly manner.

Rust gives us some features for managing code:

  • Packages: A feature of Cargo that helps you build, test, and share crates
  • Crates: A tree of modules that generate libraries or executables
  • Modules and Use: Used to control code organization, scope, and private paths
  • Paths: naming of structs, functions, and modules

Let’s take a look at how these features help us organize our code.

Packages and Crates

Package can be understood as a project, while Crate can be understood as a code base. Crate is available for multiple projects. How are package and Crate defined in our project?

Instead of using IDEA to create a new project, we’ll use cargo on the command line.

$ cargo new hello-world
     Created binary (application) `hello-world` package
$ ls hello-world
Cargo.toml
src
$ ls hello-world/src
main.rs
Copy the code

As you can see, after we created the project using cargo, there were only two files, cargo. Toml and main.rs in the SRC directory.

Cargo. Toml is the file that manages project dependencies, and each Cargo. The existence of the main.rs file indicates that the package contains a binary Crate, which is the entry file for binary Crate and has the same name as the package. If the lib.rs file exists in the SRC directory, package contains a library crate with the same name as package.

A package can contain more than one binary Crate, defined by a file in the SRC /lib directory. If your project wants to reference someone else’s Crate, add a dependency to the Cargo. Toml file. Each Crate has its own namespace, so if you import a function named Hello from crate, you can still add a function named Hello to your crate.

Module

Module helps organize code in Crate, and is an important tool for encapsulating code. The next step is to explore the Module in detail through a chestnut.

As mentioned earlier, crate is defined in the SRC /lib.rs file. Create a package that contains the crate:

cargo new --lib restaurant
Copy the code

Then define some modules and functions in SRC.

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() {}}}Copy the code

As you can see, we used the keyword mod to define Module, and Module can continue to define Module or function. In this way, we can easily put related functions into a Module and name the Module to improve the readability of the code. Structs and enumerations can also be defined in a Module. Because modules can be nested to define child modules, we end up defining code that looks like a tree.

So how do you access functions in Module? Which brings us to Path. This part is easier to understand. The Module tree is the system file directory, and Path is the directory’s Path.

Path

Like the system file path, there are two types of paths: relative path and absolute path. The absolute path must start with Crate because it codes for the root node of the entire Module tree. A double colon is used between paths to indicate references.

Now I’ll try calling add_to_waitlist in a function:

The hosting function add_to_waitlist is private. The hosting function add_to_waitlist is private. Putting this error aside for a moment, we know that when we define a Module, it is private by default, and we can use this method to encapsulate some of the implementation details of the code.

OK, back to the question, how do we fix this bug? Everyone on earth knows that the corresponding modules and functions should be disclosed. The key word in Rust that identifies a module or function as public is pub.

We use the pub keyword to expose the corresponding modules and functions

This allows us to call functions inside the Module from outside the module.

Private rules in Rust

Now let’s go back to some of the private rules in Rust. If you try the examples above, you might find something.

Private rules in Rust apply to all items (functions, methods, constructs, enumerations, modules, and constants), which are private by default. Items in a parent module cannot access private items in a child module, whereas items in a child module can access items in its parent module and above.

Private nature of Struct and Enum

Structs have a slightly different private nature than enums. For structs, I can make only some of the fields public, while others can remain private.

mod back_of_house {
    pub struct Breakfast {
        pub toast: String,
        seasonal_fruit: String,}impl Breakfast {
        pub fn summer(toast: &str) -> Breakfast {
            Breakfast {
                toast: String::from(toast),
                seasonal_fruit: String::from("peaches"),}}}}pub fn eat_at_restaurant() {
    // Order a breakfast in the summer with Rye toast
    let mut meal = back_of_house::Breakfast::summer("Rye");
    // Change our mind about what bread we'd like
    meal.toast = String::from("Wheat");
    println!("I'd like {} toast please", meal.toast);
}
Copy the code

With enUms, if an Enum is public, all of its values are public, because private values are meaningless.

Relative path and absolute path selection

This choice is not right or wrong, only appropriate. So we’re just going to illustrate some of the appropriate cases here.

Using the above code as an example, if we can foresee moving the front_of_house module and eat_at_restaurant function to a new module called customer_Experience, we should use relative paths so that we can adjust them.

Similarly, if we need to move the eat_at_restaurant function into the dining module, we don’t need to make adjustments if we choose the absolute path.

To sum up, we need to have some perspective on where our code should be optimized to determine whether relative or absolute paths are needed.

In addition to starting with the current module, relative paths can also start with super. It represents a parent module, similar to two dots in a file system (..) .

Use keyword

Absolute and relative paths can help us find the given function, but they can also be very cumbersome to use, writing a long list of paths each time. Fortunately Rust provides us with the use keyword. The import keyword is found in many languages, and here use is somewhat similar to import. But Rust provides richer usage.

The most basic use of use is to introduce a path. We can make it easier to use some of the methods in this path:

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}}}use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
}
Copy the code

The path can be an absolute path or a relative path, but if it is a relative path, it must start with self. The above example can be written as:

use self::front_of_house::hosting;
Copy the code

This seems to contradict the relative path we discussed earlier, and Rust officials say they will address this issue in a later release.

Use can go further and refer directly to a specific function or Struct or Enum. But when we use a function, use is followed by a path, so that we know which module it belongs to when we call it. When you use Struct/Enum, you refer specifically to them. Of course, this is just the official recommendation, and you can make your own, but it’s best to follow the official recommendation or project contract.

For some submodules in the same path, you can merge them into one line when importing them, for example:

use std::io;
use std::cmp::Ordering;
/ / equivalent to the
use std::{cmp::Ordering, io};
Copy the code

Sometimes we will encounter the situation of referring to structs with the same name in different packages. In this case, there are two solutions. One is to not specify the specific Struct and add different paths when using it. The second is to alias the Struct using the AS keyword.

Method one:

use std::fmt;
use std::io;

fn function1() -> fmt::Result {
    // --snip--
}

fn function2() -> io::Result< > () {// --snip--
}
Copy the code

Method 2:

use std::fmt::Result;
use std::io::Result as IoResult;

fn function1() - >Result {
    // --snip--
}

fn function2() -> IoResult<()> {
    // --snip--
}

Copy the code

If you want to import all modules or functions in a path, you can use * to indicate that. Of course, I highly recommend not using this method, because importing all of them makes it very difficult to troubleshoot problems if there are name conflicts.

For external dependencies, we need to add the dependencies in the Cargo. Toml file, and then we can use use in our code to introduce paths in the dependency library. Rust provides several standard libraries, namely libraries under STD. There is no need to add dependencies when using these libraries.

Some students see here may start to complain, said that the introduction of how to split the file, still in a file to play, this is not cheating readers.

Don’t worry, let’s start breaking it down.

Began to break up

Let’s take that piece of code for example

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() {}}}Copy the code

First, we can split the contents of the front_of_house module. We need to create a new front_of_house.rs file in the SRC directory, and then write the contents of the front_of_house module to the file. In the lib.rs file, you only need to declare the front_of_house module, no definition is required. When declaring a module, change the curly braces to a semicolon.

mod front_of_house;
Copy the code

We can then split the hosting and serving modules under the front_of_house module by creating a new file named front_of_house and placing the same name of the module in that folder. The front_of_house.rs file will also keep only declarations.

The split file directory is shown in the figure

This article focuses on the concepts and usage of Package, Crate, Module, and Path in Rust. With these foundations, it is possible to develop larger projects later.

Ps: The code examples for this article are from the Book.