English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Any programming language that cannot organize code is difficult to delve into, and almost no software product is compiled from a single source file.
All the programs in this tutorial so far have been written in a single file, mainly to facilitate the learning of Rust language syntax and concepts.
For a project, organizing code is very important.
There are three important organization concepts in Rust: crate, package, and module.
"Box" is a binary program file or library file that exists in the "package".
"Box" is a tree structure, with the root being the program compiled from the source file that the compiler starts to run.
Note: The "binary program file" is not necessarily a "binary executable file", but can only be determined to be a file containing the target machine language, and the file format varies with the compilation environment.
When we use Cargo to execute the new command to create a Rust project, a Cargo.toml file will be established under the project directory. The essence of the project is a package, which must be managed by a Cargo.toml file. This file describes the basic information and dependencies of the package.
A package can contain at most one library "box", and can contain any number of binary "boxes", but it must contain at least one "box" (whether it is a library or a binary "box").
After creating a package using the cargo new command, a main.rs source file will be generated under the src directory, and Cargo defaults this file to be the root of the binary box. The compiled binary box will have the same name as the package name.
For a software engineering project, we often organize according to the organization specification of the programming language used. The main structure of the organization module is often a tree. The main unit of the Java organization functional module is a class, while the main way to organize modules in JavaScript is function.
These advanced language organization units can be nested layer by layer, just like the directory structure of a file system. The organization unit in Rust is a module (Module).
mod nation { mod government { fn govern() {} } mod congress { fn legislate() {} } mod court { fn judicial() {} } }
This is a procedure describing a rule-of-law state: the state (nation) includes the government (government), the parliament (congress), and the court (court), which have administrative, legislative, and judicial functions respectively. We can represent it as a tree structure:
nation ├── government │ └── govern ├── congress │ └── legislate └── court └── judicial
In a file system, the directory structure is often represented by the slash in the path string to indicate the position of the object, and the path separator in Rust is ::.
Paths are divided into absolute paths and relative paths. The absolute path starts with the crate keyword. The relative path starts with the self or super keyword or an identifier. For example:
crate::nation::government::govern();
Is the absolute path describing the govern function, and the relative path can be represented as:
nation::government::govern();
Now you can try to define a similar module structure in a source program and use the path in the main function.
If you do this, you will definitely find that there are mistakes: the government module and its functions are private (private), and you are not allowed to access them.
In Rust, there are two simple access rights: public (public) and private (private).
By default, if no modifier is added, the access rights of the members in the module will be private.
To use public access rights, the pub keyword must be used.
For private modules, access is only possible from positions at the same level or lower, not from outside.
mod nation { pub mod government { pub fn govern() {} } mod congress { pub fn legislate() {} } mod court { fn judicial() { super::congress::legislate(); } } } fn main() { nation::government::govern(); }
This program can be compiled. Please observe the access method of super in the court module.
If a structure is defined within a module, both the structure itself and its fields are private by default. Therefore, to use the structure and its fields from within the module, a pub declaration is required:
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() { let mut meal = back_of_house::Breakfast::summer("Rye"); meal.toast = String::from("Wheat"); println!("I'd like {} toast please", meal.toast); } fn main() { eat_at_restaurant() }
Run Result:
I'd like Wheat toast please
Enum class enum items can contain fields, but do not have similar properties:
mod SomeModule { pub enum Person { King { name: String }, Quene } } fn main() { let person = SomeModule::Person::King { name: String::from("Blue") }; match person { SomeModule::Person::King { name } => { println!("{}", name); } _ => {} } }
Run Result:
Blue
Developers who have used Java often hate the outermost class block when programming—it has the same name as the file name, because it represents the file container. Although it is cumbersome, we have to write it once to emphasize 'this class is the class contained in the file'.
However, there are some benefits: at least it makes developers clearly aware of the existence of class wrappers, and can explicitly describe the inheritance relationship of classes.
In Rust, modules are like class wrappers in Java, but you can write a main function at the beginning of the file. How can this be explained?
The content of each Rust file is a 'hard to find' module.
Let's use two files to illustrate this:
Run Result:
This is the main module. This is the 2nd module.
The 'use' keyword can introduce module identifiers into the current scope:
mod nation { pub mod government { pub fn govern() {} } } use crate::nation::government::govern; fn main() { govern(); }
This program can be compiled successfully.
Because the 'use' keyword imports the 'govern' identifier into the current module, it can be used directly.
This solves the problem of long local module paths.
Of course, in some cases, there may be two identical names that need to be imported, and we can use the 'as' keyword to add aliases to identifiers:
mod nation { pub mod government { pub fn govern() {} } pub fn govern() {} } use crate::nation::government::govern; use crate::nation::govern as nation_govern; fn main() { nation_govern(); govern(); }
There are two 'govern' functions here, one under 'nation' and one under 'government'. We use 'as' to alias the one under 'nation' as 'nation_govern'. Both names can be used simultaneously.
The 'use' keyword can be used with the 'pub' keyword:
mod nation { pub mod government { pub fn govern() {} } pub use government::govern; } fn main() { nation::govern(); }
Rust Official Standard Library Dictionary:https://doc.rust-lang.org/stable/std/all.html
After learning the concepts in this chapter, we can easily import system libraries to facilitate program development:
use std::f64::consts::PI; fn main() { println!("{}", (PI / 2.0).sin()); }
Run Result:
1
All system library modules are imported by default, so you can easily use them by simply using the 'use' keyword to simplify the path when using them.