文章目錄
- 1. 前言
- 2. 包和 Crate
- 3. 定義模塊以及模塊之間的關系
- 4. 作用域問題
- 4.1. 作用域問題初現
- 4.2. 解決問題一
- 4.3. 解決問題二
- 4.4. super 關鍵字
- 4.5. 將路徑引入作用域
- 4.6. as 關鍵字
- 4.7. pub use 重導出
- 5. 引入的問題
- 5.1. 引入一個外部包
- 5.2. 嵌套路徑來消除大量的 use 行
- 5.3. 通過 glob 運算符將所有的公有定義引入作用域
- 6. 將模塊拆分為多文件
- 7. 小結
1. 前言
經過上一小節無聊又有趣的洗禮相信大家已經提起精神進入下一個內容的學習啦~~
這小節將會了解 Rust
中是以什么樣的形式和工具來組織和管理自己的代碼。我們都知道當代碼量和源文件數量達到一定程度時候,井然有序的組織將變得尤為重要,不然我們的代碼在外人眼里看來就是一坨*
,不僅其他人難以閱讀,作為開發者的你回頭看去也是一頭霧水,悔恨自己寫的這是個什么玩意兒。
在 Rust
中有著嚴格的作用域限制,有這樣的一個模塊系統(the model system
)來管理作用域,其中包括:
- 包(
Packages
):Cargo
的一個功能,它允許你構建、測試和分享crate
; - Crates :一個模塊的樹形結構,它形成了庫或二進制項目;
- 模塊(
Modules
)和 use:允許你控制作用域和路徑的私有性; - 路徑(
path
):一個命名例如結構體、函數或模塊等項的方式。
2. 包和 Crate
crate
是 Rust
在編譯時最小的代碼單位。如果你用 rustc
而不是 cargo
來編譯一個文件,編譯器還是會將那個文件認作一個 crate
。crate
可以包含模塊,模塊可以定義在其他文件,然后和 crate
一起編譯。
crate
有兩種形式:二進制項和庫。二進制項可以被編譯為可執行程序,比如一個命令行程序或者一個 web server
。它們必須有一個 main
函數來定義當程序被執行的時候所需要做的事情。目前我們所創建的 crate
都是二進制項。
庫并沒有 main
函數,它們也不會編譯為可執行程序,庫可以提供一些函數或結構體之類的,就如之前我們所使用過的 rand
庫就為我們提供了隨機數函數。
包(package
)是提供一系列功能的一個或者多個 crate
。一個包會包含一個 Cargo.toml
文件,闡述如何去構建這些 crate
。Cargo
就是一個包含構建你代碼的二進制項的包。Cargo
也包含這些二進制項所依賴的庫。其他項目也能用 Cargo
庫來實現與 Cargo
命令行程序一樣的邏輯。
包中可以包含至多一個庫 crate
(library crate
)。包中可以包含任意多個二進制 crate
(binary crate
),但是必須至少包含一個 crate
(無論是庫的還是二進制的)。
讓我們來看看創建包的時候會發生什么。首先,我們輸入命令 cargo new my-project
:
$ cargo new my-projectCreated binary (application) `my-project` package
$ ls my-project
Cargo.toml
src
$ ls my-project/src
main.rs
Cargo
給我們創建了什么,Cargo
會給我們的包創建一個 Cargo.toml
文件。查看 Cargo.toml
的內容,會發現并沒有提到 src/main.rs
,因為 Cargo
默認 src/main.rs
就是一個與包同名的二進制 crate
的 crate
根。
同樣執行 cargo new --lib my-library
會有同樣的目錄結構生成,不同的是這里的 src/main.rs
變成了 src/lib.rs
,并且 src/lib.rs
就是 crate
根。crate
根文件將由 Cargo
傳遞給 rustc
來實際構建庫或者二進制項目。
這里只是包含一個 src/main.rs
的包,意味著它只含有一個名為 my-project
的二進制 crate
。如果一個包同時含有 src/main.rs
和 src/lib.rs
,則它有兩個 crate
:一個二進制的和一個庫的,且名字都與包相同。通過將文件放在 src/bin
目錄下,一個包可以擁有多個二進制 crate
:每個 src/bin
下的文件都會被編譯成一個獨立的二進制 crate
。
做個簡單的實驗,首先執行 cargo new multiple_bin
$ cargo new multiple_bin
Creating binary (application) `multiple_bin` package
note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
$ cd multiple_bin
$ tree
.
├── Cargo.lock
├── Cargo.toml
└── src└── main.rs
進入 src
,創建 bin
目錄,并在其中創建多個 .rs
文件:
$ cd src
$ mkdir bin
$ cp ../main.rs bin01.rs
$ cp ../main.rs bin02.rs
$ cd ../../
編譯該項目:
$ cargo buildCompiling multiple_bin v0.1.0 (/home/im01/Miracle/rust/multiple_bin)Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.11s
$ tree
.
├── Cargo.lock
├── Cargo.toml
├── src
│ ├── bin
│ │ ├── bin01.rs
│ │ └── bin02.rs
│ └── main.rs
└── target├── CACHEDIR.TAG└── debug├── bin01├── bin01.d├── bin02├── bin02.d├── build├── deps│ ├── bin01-54a8a90b566c47ce│ ├── bin01-54a8a90b566c47ce.d│ ├── bin02-057c31f4258a913d│ ├── bin02-057c31f4258a913d.d│ ├── multiple_bin-79155e437d2fa379│ └── multiple_bin-79155e437d2fa379.d├── examples├── incremental......├── multiple_bin└── multiple_bin.d15 directories, 48 files
[注]:這里為了簡潔僅展示了部分主要內容。
從 target
目錄可以看出這里不僅會為 main.rs
生成了與根同名的 multiple_bin
二進制的 crate
,還會為在 bin
目錄下的兩個文件生成對應文件名的二進制 crate
。
3. 定義模塊以及模塊之間的關系
這里將會涉及到幾個重要的關鍵字:
use
:將路徑引入作用域;pub
:使對應項變為公有性質;as
:為同名函數起別名。
在此之前先對幾個概念做以解釋:
rust
項目是從crate
根節點開始檢索代碼:這很好理解,對于一個二進制crate
的根就是src/main.rs
,而庫則是src/lib.rs
,就類似在C/C++
中總是以main
函數開始;- 聲明模塊:在
crate
根文件中用mod
關鍵字可以聲明一個模塊,如:
mod xiaomi_car; // 中國最大的保時捷&法拉利元素融合高性能新能源汽車集團
這便是聲明了一個 xiaomi_car
模塊,而當 mod xiaomi_car
后是一個大括號時,這樣的方式成為內聯,如:
mod xiaomi_car{fn sale() {} // 銷售部銷售小米汽車,金牌銷售員:雷將軍}
- 聲明子模塊:在不是
main.rs
中定義的模塊被稱為子模塊; - 公有和私有:一個模塊里的代碼默認對其夫模塊私有。為一個模塊加上
pub
關鍵字即使用pub mod
來聲明模塊,則表示將該模塊公有化;
看完這些名詞解釋相信大家在大腦中還是一團漿糊,那么接下來將通過一點小實驗逐步讓我們梳理清楚現在正在學習的到底是什么。
當下火出天際的汽車行業的雷小軍的保時米為例來介紹,保時米集團有這樣的部門專門用來營銷宣傳以及銷售汽車被稱為銷售部(Sales Department
),還有專門用來生產制造的制造部(Manufacturing Department
)。
好了,現在讓我們來創建一個小米汽車的庫,起名為 xiaomi_car
。
$ cargo new --lib xiaomi_carCreating library `xiaomi_car` package
note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
編輯 src/lib.rs
文件添加如下內容:
mod sales_department {// 車輛售前服務mod pre_sales_service {// 車型咨詢fn model_consultation() {}// 制定購車方案fn purchase_plan_options() {}}// 車輛售后服務mod after_sales_service {// 車輛保養服務fn vehicle_maintenance_services() {}// 維修服務fn repair_services() {}}
}
這里我們用關鍵字 mod
定義了一個名為 sales_department
的模塊,接著花括號內為該模塊的主體部分。在模塊內仍然可以指定其它模塊,正如這里的 pre_sales_service
和 after_sales_service
模塊是屬于 sales_department
的子模塊,相應的 sales_department
是他們的父模塊。而模塊內還可以定義一些其它的各種類型,如結構體、枚舉、常量、函數等。
通過以模塊的方式將相關的代碼定義再一起,這樣會更有組織性的管理程序,以 src/main.rs
或 src/lib.rs
作為 crate
根,構成了一整個模塊樹(module tree
)。
上面的代碼就展示如下這樣結構的設備樹。
crate└── sales_department ├── pre_sales_service │ ├── model_consultation│ └── purchase_plan_options└── after_sales_service ├── vehicle_maintenance_services└── repair_services
在 rust
里仍然是借用家庭關系來描述模塊之間的關系,在同一個模塊中定義的子模塊互為兄弟模塊(siblings module
),而包含著子模塊的模塊稱為他們的父模塊(parent module
)。注意,整個模塊樹都植根于名為 crate
的隱式模塊下。這樣一來我們就可以更加清晰的設計和組織我們的代碼。
4. 作用域問題
4.1. 作用域問題初現
上面我們了解到模塊之間的結構可以被抽象成為樹狀結構,那么不同層之間的模塊是否能夠相互調用呢?(既然已經這么問了,那么一定是不可以咯~)總而言是,先試試看吧。
// 保時米的銷售部門
mod sales_department {// 車輛售前服務mod pre_sales_service {// 車型咨詢fn model_consultation() {}// 制定購車方案fn purchase_plan_options() {}// 添加到生產訂單fn add_to_production_order() {}}// 車輛售后服務mod after_sales_service {// 車輛保養服務fn vehicle_maintenance_services() {}// 維修服務fn repair_services() {}}
}// 保時米的生產制造部門
mod manufacturing_department {// 生產計劃mod production_planning {// 制定生產計劃fn specify_production_plan() {// 添加到生產訂單add_to_production_order();}}// 總裝車間mod final_assembly_workshop {}
}
讓我們就這樣編譯看下能不能通過:
im01@Ubuntu:xiaomi_car$ cargo buildCompiling xiaomi_car v0.1.0 (/home/im01/Miracle/rust/xiaomi_car)
error[E0425]: cannot find function `add_to_production_order` in this scope--> src/lib.rs:23:13|
23 | add_to_production_order();| ^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope|
note: function `crate::sales_department::pre_sales_service::add_to_production_order` exists but is inaccessible--> src/lib.rs:7:9|
7 | fn add_to_production_order() {}| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not accessibleFor more information about this error, try `rustc --explain E0425`.
error: could not compile `xiaomi_car` (lib) due to 1 previous error
哦~哦吼吼~,不出意外的出意外了,從錯誤信息中可以看到,沒有在當前范圍內找到 add_to_production_order
這個函數,再看到下面的提示告訴我們說,有一個同名的函數存在,但是不可以訪問。注意一下,這里有兩點問題:
- 第一:我們函數調用的方式不對,因為調用了一個不存在的函數;
- 第二:即便我們去調用這個存在的函數,同樣存在不可訪問的問題。
4.2. 解決問題一
根據提示我們可以解決第一個問題,修改上面代碼:
mod sales_department {mod pre_sales_service {//-------------snip----------------fn add_to_production_order() {}//-------------snip----------------}
}mod manufacturing_department {mod production_planning {fn specify_production_plan() {//add_to_production_order();// 絕對路徑crate::sales_department::pre_sales_service::add_to_production_order();}}mod final_assembly_workshop {}
}pub fn sales_announcement() {// 相對路徑sales_department::pre_sales_service::add_to_production_order();
}
[注]:此處為了突出主要內容,省略了部分無關代碼。這里為了說明相對路徑問題額外添加 sales_announcement 函數。
這里引入兩個概念:
- 絕對路徑: 從
crate
開始,即從根開始按照樹狀結構索引出來的,每一層之間用::
隔開; - 相對路徑: 從當前位置出發,即從同一層的模塊位置出發索引,同樣每層之間用
::
隔開。
好了,這里我們編譯一下,看看會出現什么問題:
im01@Ubuntu:xiaomi_car$ cargo buildCompiling xiaomi_car v0.1.0 (/home/im01/Miracle/rust/xiaomi_car)
error[E0603]: module `pre_sales_service` is private--> src/lib.rs:25:38|
25 | crate::sales_department::pre_sales_service::add_to_production_order();| ^^^^^^^^^^^^^^^^^ ----------------------- function `add_to_production_order` is not publicly re-exported| || private module|
note: the module `pre_sales_service` is defined here--> src/lib.rs:2:5|
2 | mod pre_sales_service {| ^^^^^^^^^^^^^^^^^^^^^error[E0603]: module `pre_sales_service` is private--> src/lib.rs:37:23|
37 | sales_department::pre_sales_service::add_to_production_order();| ^^^^^^^^^^^^^^^^^ ----------------------- function `add_to_production_order` is not publicly re-exported| || private module|
note: the module `pre_sales_service` is defined here--> src/lib.rs:2:5|
2 | mod pre_sales_service {| ^^^^^^^^^^^^^^^^^^^^^For more information about this error, try `rustc --explain E0603`.
error: could not compile `xiaomi_car` (lib) due to 2 previous errors
錯誤信息中告訴我們 pre_sales_service
是一個私有模塊,被 sales_department
模塊私有,因此外部無法訪問(這里所指的外部是 pre_sales_service
同層之外)。
4.3. 解決問題二
此時的整個模塊樹看起來是這樣的。
crate├── sales_department │ ├── pre_sales_service │ │ ├── model_consultation│ │ ├── purchase_plan_options│ │ └── add_to_production_order│ └── after_sales_service │ ├── vehicle_maintenance_services│ └── repair_services│└── manufacturing_department ├── production_planning │ └── specify_production_plan└── final_assembly_workshop
要想使得外部也可以訪問,這里就需要使用到關鍵字 pub
。
mod sales_department {pub mod pre_sales_service {fn model_consultation() {}fn purchase_plan_options() {}fn add_to_production_order() {}}mod after_sales_service {fn vehicle_maintenance_services() {}fn repair_services() {}}
}//---------------snip---------------------------
那么只在 pre_sales_service
前加上 pub
是否就可以了呢?編譯試試看:
im01@Ubuntu:xiaomi_car$ cargo buildCompiling xiaomi_car v0.1.0 (/home/im01/Miracle/rust/xiaomi_car)
error[E0603]: function `add_to_production_order` is private--> src/lib.rs:25:57|
25 | crate::sales_department::pre_sales_service::add_to_production_order();| ^^^^^^^^^^^^^^^^^^^^^^^ private function|
note: the function `add_to_production_order` is defined here--> src/lib.rs:7:9|
7 | fn add_to_production_order() {}| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^error[E0603]: function `add_to_production_order` is private--> src/lib.rs:37:42|
37 | sales_department::pre_sales_service::add_to_production_order();| ^^^^^^^^^^^^^^^^^^^^^^^ private function|
note: the function `add_to_production_order` is defined here--> src/lib.rs:7:9|
7 | fn add_to_production_order() {}| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^For more information about this error, try `rustc --explain E0603`.
error: could not compile `xiaomi_car` (lib) due to 2 previous errors
正如之前所說,外部無法訪問指的外部是同層之外。因此 add_to_production_order
函數前也需要加上 pub
。
mod sales_department {pub mod pre_sales_service {fn model_consultation() {}fn purchase_plan_options() {}pub fn add_to_production_order() {}}mod after_sales_service {fn vehicle_maintenance_services() {}fn repair_services() {}}
}//---------------snip---------------------------
這樣一來編譯就沒問題了。到這里相信大家已經粗略的感受到了在 rust
中作用域的概念。
4.4. super 關鍵字
這里換一種方式去調用 add_to_production_order
。
mod sales_department {pub mod pre_sales_service {fn model_consultation() {}fn purchase_plan_options() {}pub fn add_to_production_order() {}}mod after_sales_service {fn vehicle_maintenance_services() {}fn repair_services() {}}
}mod manufacturing_department {mod production_planning {fn specify_production_plan() {//add_to_production_order();crate::sales_department::pre_sales_service::add_to_production_order();// 等同于上一行super::super::sales_department::pre_sales_service::add_to_production_order();}}mod final_assembly_workshop {}
}pub fn sales_announcement() {sales_department::pre_sales_service::add_to_production_order();
}
此處便用到了 super
關鍵字,其作用想必各位從形勢中也窺竊出一二來,沒錯 super
關鍵字的作用類似與 Linux
文件系統中的 ..
語法——到上一級目錄,而相應的這里則是到上一級模塊層。
為什么 rust
會多此一舉的設計這樣的關鍵字,原因很簡單,當全文都在使用絕對路徑,這樣沒錯,但會顯得代碼冗長。而全文又使用相對路徑,則會導致邏輯看起來混亂,難以閱讀,一旦代碼做過改動尤其是移動之后,將會帶來相應的錯誤,而定位起來也較為不便,那么有了 super
的引入之后,我們很清楚的知道模塊之間的關系,當代碼整體移動時,也不必擔心路徑不對而需要修改調用路徑。就是這樣。
4.5. 將路徑引入作用域
上面兩個問題的解決方法雖然有效,但是仍然讓代碼顯得冗長,這里將會介紹一個關鍵字 use
直接將需要的路徑引入當前作用域,可以極大的簡化代碼的長度。
mod sales_department {pub mod pre_sales_service {fn model_consultation() {}fn purchase_plan_options() {}pub fn add_to_production_order() {}}
}mod manufacturing_department {mod production_planning {// 使用 use 關鍵字use crate::sales_department::pre_sales_service;fn specify_production_plan() {//add_to_production_order();pre_sales_service::add_to_production_order();}}
}
在當前作用域中增加 use
和路徑類似于在文件系統中創建軟連接(符號連接,symbolic link
)。需要注意的是,use
只能對當前作用域范圍內有效,若上面代碼改為如下這樣,則會無法使用到 use
引入進來的路徑:
mod sales_department {pub mod pre_sales_service {fn model_consultation() {}fn purchase_plan_options() {}pub fn add_to_production_order() {}}
}// 使用 use 關鍵字
use crate::sales_department::pre_sales_service;mod manufacturing_department {mod production_planning {fn specify_production_plan() {//add_to_production_order();pre_sales_service::add_to_production_order();}}
}
事實上 use
也可以直接將其全部路徑都引入,像這樣:
mod sales_department {pub mod pre_sales_service {pub fn add_to_production_order() {}}
}mod manufacturing_department {mod production_planning {use crate::sales_department::pre_sales_service::add_to_production_order;fn specify_production_plan() {add_to_production_order();//pre_sales_service::add_to_production_order();}}
}
直接引入 add_to_production_order
函數的完整路徑,這樣的作法是允許的,而常常的做法會像之前一樣,引入到其上一級,這樣編寫的代碼會很明確看出來該函數是別的模塊,而非當前模塊。
4.6. as 關鍵字
這樣引入完整路徑會有什么問題嗎?假設有兩個同名函數不同模塊,被同時引入呢?
答案是,rust
編譯器將會告訴你這樣的操作是不允許的。
那的確出現這樣的情況怎么做?這時候 rust
提供了我們另外一個關鍵字 as
,他可以為引入的變量或函數起別名,就像這樣:
mod sales_department {pub mod pre_sales_service {pub fn test_func() {}}pub mod after_sales_service {pub fn test_func() {}}
}use crate::sales_department::pre_sales_service::test_func as pre_test_func;
use crate::sales_department::after_sales_service::test_func as after_test_func;
pub fn sales_announcement() {pre_test_func();after_test_func();
}
使用 as
關鍵字讓兩個本來會使用沖突的函數同時可以引入當前作用域。
4.7. pub use 重導出
mod sales_department {pub mod pre_sales_service {pub fn add_to_production_order() {}}
}pub use crate::sales_department::pre_sales_service;pub fn sales_announcement() {pre_sales_service::add_to_production_order();
}
這段代碼與之前有所不同,這里引入路徑使用了 pub use
而非 use
,這樣的作用是為了讓外部代碼也可以用這樣的路徑。
使用 use
關鍵字
在使用 use
引入路徑時,外部代碼調用 add_to_production_order
需要指定其完整路徑xiaomi_car::sales_department::pre_sales_service::add_to_production_order()
才能夠調用該函數;
使用 pub use
關鍵字
當使用 pub use
引入時,外部代碼則可以通過 xiaomi_car::pre_sales_service::add_to_production_order()
來調用該函數,仔細觀察二者的區別,這樣可以省略中間的一大堆具體路徑,何樂而不為呢。
5. 引入的問題
5.1. 引入一個外部包
在之前我們編寫了一個猜猜看游戲。那個項目使用了一個外部包,rand
,來生成隨機數。為了在項目中使用 rand
,在 Cargo.toml
中加入了如下行:
rand = "0.8.5"
在 Cargo.toml
中加入 rand
依賴告訴了 Cargo
要從 crates.io
下載 rand
和其依賴,并使其可在項目代碼中使用。
接著,為了將 rand
定義引入項目包的作用域,我們加入一行 use
起始的包名,它以 rand
包名開頭并列出了需要引入作用域的項。回憶一下之前的 “生成一個隨機數” 部分,我們曾將 Rng trait
引入作用域并調用了 rand::thread_rng
函數:
use rand::Rng;fn main() {let secret_number = rand::thread_rng().gen_range(1..=100);
}
crates.io
上有很多 Rust
社區成員發布的包,將其引入你自己的項目都需要一道相同的步驟:在 Cargo.toml
列出它們并通過 use
將其中定義的項引入項目包的作用域中。
注意 std
標準庫對于你的包來說也是外部 crate
。因為標準庫隨 Rust
語言一同分發,無需修改 Cargo.toml
來引入 std
,不過需要通過 use
將標準庫中定義的項引入項目包的作用域中來引用它們,比如我們使用的 HashMap
:
use std::collections::HashMap;
這是一個以標準庫 crate
名 std
開頭的絕對路徑。
5.2. 嵌套路徑來消除大量的 use 行
當需要引入很多定義于相同包或相同模塊的項時,為每一項單獨列出一行會占用源碼很大的空間。例如猜猜看代碼中有兩行 use
語句都從 std
引入項到作用域:
// --snip--
use std::cmp::Ordering;
use std::io;
// --snip--
相反,我們可以使用嵌套路徑將相同的項在一行中引入作用域。這么做需要指定路徑的相同部分,接著是兩個冒號,接著是大括號中的各自不同的路徑部分:
// --snip--
use std::{cmp::Ordering, io};
// --snip--
在較大的程序中,使用嵌套路徑從相同包或模塊中引入很多項,可以顯著減少所需的獨立 use
語句的數量!
我們可以在路徑的任何層級使用嵌套路徑,這在組合兩個共享子路徑的 use
語句時非常有用。例如有兩個 use
語句:一個將 std::io
引入作用域,另一個將 std::io::Write
引入作用域:
use std::io;
use std::io::Write;
兩個路徑的相同部分是 std::io
,這正是第一個路徑。為了在一行 use
語句中引入這兩個路徑,可以在嵌套路徑中使用 self
:
use std::io::{self, Write};
這一行代碼便將 std::io
和 std::io::Write
同時引入作用域。
5.3. 通過 glob 運算符將所有的公有定義引入作用域
如果希望將一個路徑下所有公有項引入作用域,可以指定路徑后跟 *
——glob
運算符:
use std::collections::*;
這個 use
語句將 std::collections
中定義的所有公有項引入當前作用域。使用 glob
運算符時請多加小心!Glob
會使得我們難以推導作用域中有什么名稱和它們是在何處定義的。
glob
運算符經常用于測試模塊 tests
中,這時會將所有內容引入作用域。
6. 將模塊拆分為多文件
到目前為止,所有的例子都在一個文件中定義多個模塊。當模塊變得更大時,你可能想要將它們的定義移動到單獨的文件中,從而使代碼更容易閱讀。
為了避免產生歧義,這里貼出筆者希望拆分的原本代碼:
mod sales_department {pub mod pre_sales_service {fn model_consultation() {}fn purchase_plan_options() {}pub fn add_to_production_order() {}}pub mod after_sales_service {fn vehicle_maintenance_services() {}fn repair_services() {}}
}mod manufacturing_department {mod production_planning {fn specify_production_plan() {crate::sales_department::pre_sales_service::add_to_production_order();}}mod final_assembly_workshop {}
}pub fn sales_announcement() {
sales_department::pre_sales_service::add_to_production_order();
}
首先嘗試將 sales_department
、manufacturing_department
模塊拆分出去,首先在 src
目錄下創建 sales_department.rs
文件,為其添加如下內容:
pub mod pre_sales_service {fn model_consultation() {}fn purchase_plan_options() {}pub fn add_to_production_order() {}
}pub mod after_sales_service {fn vehicle_maintenance_services() {}fn repair_services() {}
}
[注]:這里是 src/sales_department.rs 文件
然后創建 manufacturing_department.rs
文件,為其添加如下內容:
mod production_planning {fn specify_production_plan() {crate::sales_department::pre_sales_service::add_to_production_order();}
}mod final_assembly_workshop {
}
[注]:這里是 src/manufacturing_department.rs 文件
然后再修改 src/lib.rs
文件內容:
mod sales_department;
mod manufacturing_department;pub fn sales_announcement() {
sales_department::pre_sales_service::add_to_production_order();
}
這樣就完成了這兩個模塊的拆分。因為編譯器找到了 crate
根中名叫 sales_department
的模塊聲明,它就知道去搜尋 src/sales_department.rs
這個文件。
那如果還想繼續拆分呢?要怎么做,其實道理相同,下面筆者展示將 sales_department
模塊繼續拆分成多個文件。
首先在 src
目錄下創建 sales_department
目錄,再進入 sales_department
目錄,分別創建文件 pre_sales_service.rs
、after_sales_service.rs
,并為其添加如下內容:
fn model_consultation() {}fn purchase_plan_options() {}pub fn add_to_production_order() {}
[注]:這里是 src/sales_department/pre_sales_service.rs 文件
fn vehicle_maintenance_services() {}fn repair_services() {}
[注]:這里是 src/sales_department/after_sales_service.rs 文件
這樣一來就將該模塊繼續拆分為更多的文件,這樣拆分完后的文件目錄結構如下。這個目錄結構是不是很像我們的模塊樹。如果完全拆開,那么這就是模塊樹。
im01@Ubuntu:xiaomi_car$ tree
.
├── Cargo.lock
├── Cargo.toml
└── src├── lib.rs├── manufacturing_department├── manufacturing_department.rs├── sales_department│ ├── after_sales_service.rs│ └── pre_sales_service.rs└── sales_department.rs
各位應該看到, 這里筆者提前創建了一個 manufacturing_department
目錄,各位同學可以自己嘗試將 manufacturing_department
模塊繼續拆分。這個技巧讓你可以在模塊代碼增長時,將它們移動到新文件中。
注意我們只需在模塊樹中的某處使用一次 mod
聲明就可以加載這個文件。一旦編譯器知道了這個文件是項目的一部分(并且通過 mod
語句的位置知道了代碼在模塊樹中的位置),項目中的其他文件應該使用其所聲明的位置的路徑來引用那個文件的代碼,這在“引用模塊項目的路徑”部分有講到。換句話說,mod
不是你可能會在其他編程語言中看到的 "include"
操作。
7. 小結
Rust
提供了將包分成多個 crate
,將 crate
分成模塊,以及通過指定絕對或相對路徑從一個模塊引用另一個模塊中定義的項的方式。你可以通過使用 use
語句將路徑引入作用域,這樣在多次使用時可以使用更短的路徑。模塊定義的代碼默認是私有的,不過可以選擇增加 pub
關鍵字使其定義變為公有。
這一小節的文章很長,筆者寫的自認為也太過啰嗦,能夠堅持看完的你真的很厲害,請收下筆者贈與的小紅花🌸,萬分感激您的閱讀,有任何疑問或問題還請不吝賜教~
下一篇《【Rust】集合的使用——Rust語言基礎16》
覺得這篇文章對你有幫助的話,就留下一個贊吧v*
請尊重作者,轉載還請注明出處!感謝配合~
[作者]: Imagine Miracle
[版權]: 本作品采用知識共享署名-非商業性-相同方式共享 4.0 國際許可協議進行許可。
[本文鏈接]: https://blog.csdn.net/qq_36393978/article/details/146339937