The Rust module system is very confusing and makes novices feel a great sense of failure
In this article, I will explain the modular system with practical examples. You will clearly understand how it works and can be applied to your project immediately
Since the Rust module system is very unique, I ask you to open your mind when reading this article and not apply it to other languages
Simulate a real project with the following structure:
my_project ├── Cargo.toml └─┬ src ├── main.rs ├── config.rs ├─┬ routes │ ├── health_route.rs │ └── user_route.rs └─┬ models └── user_model.rs
There are different ways for us to use module
These 3 examples should be sufficient to explain how Rust's module system works.
Three examples should be sufficient to solve how the Rust module system works
Example 1
// main.rs fn main() { println!("main"); }
// config.rs fn print_config() { println!("config"); }
The first error is due to some files config.rs, health_route.rs, etc. we think these files are modules, and we can import them into other files
Below are the file system tree I see and the module tree seen by the compiler
Surprisingly, the compiler can only see the crite module, which is also the main.rs file. This is because we need to display the build module tree in Rust - there is no implicit mapping between the file system tree and the module tree
We need to show that building the Rust module tree has no implicit mapping to the file system
In order to add the file to the module tree, we need to declare the file as a sub module using the mod keyword. The following may also be confusing. You should assume that we declare the file as a module in the same file. But we declare it in different files. Since we only have main.rs in the module tree, let's declare config.rs as a sub module of main.rs
mod keyword declaration sub module
The syntax of the keyword mod is
mod my_module
The compiler below looks for my in the same directory_ Module.rs or my_module/mod.rs
my_project ├── Cargo.toml └─┬ src ├── main.rs └── my_module.rs or my_project ├── Cargo.toml └─┬ src ├── main.rs └─┬ my_module └── mod.rs
Since main.rs and config.rs are in the same directory, let's declare config as a module
// main.rs + mod config; fn main() { + config::print_config(); println!("main"); }
// config.rs fn print_config() { println!("config"); }
We access the function print_config:: Syntax
Now the module tree is like this
We successfully declared the config module! However, print in config.rs cannot be called yet_ Config(). Almost everything in Rust is private by default. We need to use the keyword pub to make the function public
The keyword pub can make access public
// main.rs mod config; fn main() { config::print_config(); println!("main"); }
// config.rs - fn print_config() { + pub fn print_config() { println!("config"); }
Now, with success, I succeeded in calling the defined functions in different files.
Example 2
Let's call routes/health_ from main.rs. Print in route.rs_ health_ route().
// main.rs mod config; fn main() { config::print_config(); println!("main"); }
// routes/health_route.rs fn print_health_route() { println!("health_route"); }
As mentioned earlier, using the keyword mod is just my_module.rs or my_module/mod.rs should be in the same directory
So in order to call routes/health_ from main.rs For the function in route.rs, we need to do the following:
- Create a file named routes/mod.rs, and define the routes sub module in main.rs
- Define the sub module health in routes/mod.rs_ Route and make its access rights public
- Make health_ The access permission in route.rs is public
my_project ├── Cargo.toml └─┬ src ├── main.rs ├── config.rs ├─┬ routes + │ ├── mod.rs │ ├── health_route.rs │ └── user_route.rs └─┬ models └── user_model.rs
// main.rs mod config; + mod routes; fn main() { + routes::health_route::print_health_route(); config::print_config(); println!("main"); }
// routes/mod.rs + pub mod health_route;// routes/mod.rs + pub mod health_route;
// routes/health_route.rs - fn print_health_route() { + pub fn print_health_route() { println!("health_route"); }
Below is what the module tree looks like
You can now call the functions in the files in the folder
Example 3
Let's try calling from main. Rs = > routes / user_ route.rs => models/user_ model.rs
// main.rs mod config; mod routes; fn main() { routes::health_route::print_health_route(); config::print_config(); println!("main"); }
// routes/user_route.rs fn print_user_route() { println!("user_route"); }
// models/user_model.rs fn print_user_model() { println!("user_model"); }
We want to call print_ from main. user_ Route then calls print_ user_ Functions in model
Let's do the previous again - define the sub module, modify the function access permission to common, and then add the mod.rs file
my_project ├── Cargo.toml └─┬ src ├── main.rs ├── config.rs ├─┬ routes │ ├── mod.rs │ ├── health_route.rs │ └── user_route.rs └─┬ models + ├── mod.rs └── user_model.rs
// main.rs mod config; mod routes; + mod models; fn main() { routes::health_route::print_health_route(); + routes::user_route::print_user_route(); config::print_config(); println!("main"); }
// routes/mod.rs pub mod health_route; + pub mod user_route;
// models/mod.rs + pub mod user_model;
// models/user_model.rs - fn print_user_model() { + pub fn print_user_model() { println!("user_model"); }
The module tree now grows like this
Wait, we can't actually print yet_ user_ Calling print_ in route user_ model! So far, we have just called functions defined in other modules from main.rs. What should we do in other files?
If I look at the module tree, the function print_ user_ The model is in crite:: models:: user_ So in order to use the modules in the file, not main.rs. we should think about accessing them according to the module path in the necessary module tree
// routes/user_route.rs pub fn print_user_route() { + crate::models::user_model::print_user_model(); println!("user_route"); }
We successfully invoked the functions defined in other files in non main.rs files.
super
If our organization file has multiple directories, the reference to the complete name is too long. If for some reason, we want to start from print_ user_ Calling print_ in route health_ Route. Will have the following two paths: crite:: routes:: health_ Route and crite:: routes:: user_ route.
We call it by using the full reference name crite:: routes:: health_ route::print_ health_ Route(), but we can also use the relative path super::health_route::print_health_route(). Note that super is used to point to the parent scope
The keyword super of the module path specifies the parent scope
pub fn print_user_route() { crate::routes::health_route::print_health_route(); // can also be called using super::health_route::print_health_route(); println!("user_route"); }
use
In the above example, using the full reference name or even the relative reference name is lengthy. In order to make the reference name shorter, we use the keyword use to bind the new module name or rename it
The keyword use is used to shorten the module path
pub fn print_user_route() { crate::models::user_model::print_user_model(); println!("user_route"); }
The above code can be refactored into
use crate::models::user_model::print_user_model; pub fn print_user_route() { print_user_model(); println!("user_route"); }
Replace with print_user_model name, which can be renamed
use crate::models::user_model::print_user_model as log_user_model; pub fn print_user_route() { log_user_model(); println!("user_route"); }
Extension module
Add dependencies to Cargo.toml, and all modules are available in the project. There is no need to display, introduce or declare anything else to use dependencies
Extension dependencies are globally available for modules throughout the project
For example, by adding Rand slate to the project, we can use it directly in the code
pub fn print_health_route() { let random_number: u8 = rand::random(); println!("{}", random_number); println!("health_route"); }
You can also use use to shorten the reference path
use rand::random; pub fn print_health_route() { let random_number: u8 = random(); println!("{}", random_number); println!("health_route"); }
summary
- Module systems are shown referenced - there is no relationship between one-to-one and file system mapping
- The declaration file is in his father as a module, not in himself
- The keyword mod is used to declare the submodule
- It is required to display and declare that the access rights of functions, structures, etc. are public, and all of them can be in other modules
- The keyword pub makes the code access permission common
- Third party modules do not need to be declared