Rust的项目结构

crate是Rust的最小代码单位,有二进制项和库两种形式,二进制项有main函数,库没有main函数。包(package)是提供一系列功能的一个或多个crate,一个包会包含一个Cargo.toml文件,包可以包含一个库crate(library crate)或任意多个二进制crate(binary crate),但至少需要包含一个crate。
用Cargo创建一个新的包,里面会有src文件夹和Cargo.toml文件,src文件夹里面有一个main.rs文件,这是与包同名的二进制crate根,如果有lib.rs文件,则是与包同名的库crate根。

💡 提示

将文件放在src/bin文件夹下,Cargo会自动将其视为一个独立的二进制crate,并生成一个对应的二进制crate的根。

模块应用🔗

模块化是一种非常重要的编程范式,它可以帮助开发者编写更清晰、更易于维护、更易于扩展的代码。

模块声明🔗

编译器从crate根文件(src/main.rs或src/lib.rs)开始寻找需要被编译的代码,如果你在crate根文件中声明一个新模块,例如使用mod my_module;,编译器会在crate根目录下寻找src/my_module.rs文件或src/my_module/mod.rs文件。

子模块声明🔗

如果在src/my_module.rs定义了mod my_submodule;,编译器会在src/my_module目录下寻找my_submodule.rs文件或my_submodule/mod.rs文件。

私有和公有🔗

模块的代码默认对其父模块私有,如果想让模块中的项对父模块外部可见,可以使用pub关键字。

ℹ️ 注意

将结构体定义为pub,则结构体的字段默认为私有,如果想让字段公有,需要使用pub关键字。
若结构体某个字段为私有,则需要提供一个关联函数来构建实例,否则外部无法创建实例。

use关键字🔗

use crate::my_module::my_submodule;,这会导入my_submodule模块,之后就可以直接使用my_submodule,而不需要my_module::my_submodule。

模块树🔗

考虑src/lib.rs中的这样一段代码:

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

这段代码中模块树结构如下:

crate
 └── front_of_house
     ├── hosting
     │   ├── add_to_waitlist
     │   └── seat_at_table
     └── serving
         ├── take_order
         ├── serve_order
         └── take_payment

使用模块管理代码能让代码结构更清晰,更易于维护。

引用模块🔗

要调用模块中的函数,需要用到::操作符,关于路径有绝对路径(absolute path)和相对路径(relative path)两种,绝对路径从crate开始,相对路径从当前模块开始,使用self关键字表示当前模块,super关键字表示父模块。

💡 提示

使用use crate::front_of_house::hosting;,这会导入hosting模块,之后就可以直接使用hosting,而不需要front_of_house::hosting。use语句只适用于其所在作用域。

ℹ️ 注意

一般来讲,一个包同时包含二进制crate和库crate,此时二进制crate中只保留可执行文件的代码,被调用的代码都放在库crate中,模块树也应该定义在src/lib.rs中。

使用pub use关键字可以将模块导入当前作用域后,其他模块也可以倒入,例如:

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

若不使用pub use,外部代码需要使用路径 restaurant::front_of_house::hosting::add_to_waitlist()来调用add_to_waitlist函数。现在,外部代码可以使用restaurant::hosting::add_to_waitlist()来调用add_to_waitlist函数。