目前為止,我們編寫的程序都在一個(gè)文件的一個(gè)模塊中。伴隨著項(xiàng)目的增長(zhǎng),你可以通過將代碼分解為多個(gè)模塊和多個(gè)文件來組織代碼。一個(gè)包可以包含多個(gè)二進(jìn)制 crate 項(xiàng)和一個(gè)可選的 crate 庫。伴隨著包的增長(zhǎng),你可以將包中的部分代碼提取出來,做成獨(dú)立的 crate,這些 crate 則作為外部依賴項(xiàng)。
Rust 有許多功能可以讓你管理代碼的組織,包括哪些內(nèi)容可以被公開,哪些內(nèi)容作為私有部分,以及程序每個(gè)作用域中的名字。這些功能。這有時(shí)被稱為 “模塊系統(tǒng)(the module system)”,包括:
- 包(Packages): Cargo 的一個(gè)功能,它允許你構(gòu)建、測(cè)試和分享 crate。
- Crates?:一個(gè)模塊的樹形結(jié)構(gòu),它形成了庫或二進(jìn)制項(xiàng)目。
- 模塊(Modules)和?use: 允許你控制作用域和路徑的私有性。
- 路徑(path):一個(gè)命名例如結(jié)構(gòu)體、函數(shù)或模塊等項(xiàng)的方式
7.1?包和crate
模塊系統(tǒng)的第一部分,我們將介紹包和 crate。crate 是一個(gè)二進(jìn)制項(xiàng)或者庫。crate root?是一個(gè)源文件,Rust 編譯器以它為起始點(diǎn),并構(gòu)成你的 crate 的根模塊。而包(package) 是提供一系列功能的一個(gè)或者多個(gè) crate。一個(gè)包會(huì)包含有一個(gè)?Cargo.toml?文件,闡述如何去構(gòu)建這些 crate。
一個(gè)包中至多?只能?包含一個(gè)庫 crate(library crate);包中可以包含任意多個(gè)二進(jìn)制 crate(binary crate);包中至少包含一個(gè) crate,無論是庫的還是二進(jìn)制的。
emm......這話說的
一個(gè)包至少包含一個(gè)crate,其中,可以包含任意個(gè)二進(jìn)制crate、但最多只能包含一個(gè)庫crate
看看創(chuàng)建包的時(shí)候會(huì)發(fā)生什么
具體的文件夾是這樣的
當(dāng)我們輸入了這條命令,Cargo 會(huì)給我們的包創(chuàng)建一個(gè)?Cargo.toml?文件。查看?Cargo.toml?的內(nèi)容,會(huì)發(fā)現(xiàn)并沒有提到?src/main.rs,因?yàn)?Cargo 遵循的一個(gè)約定:src/main.rs?就是一個(gè)與包同名的二進(jìn)制 crate 的 crate 根。同樣的,Cargo 知道如果包目錄中包含?src/lib.rs,則包帶有與其同名的庫 crate,且?src/lib.rs?是 crate 根。crate 根文件將由 Cargo 傳遞給?rustc
?來實(shí)際構(gòu)建庫或者二進(jìn)制項(xiàng)目。
一個(gè)包至少包含一個(gè)crate,其中可以包含任意個(gè)二進(jìn)制crate、最多只能包含一個(gè)庫crate
src/main.rs是一個(gè)與包同名的二進(jìn)制crate(也是二進(jìn)制的根crate);
如果該目錄下還存在src/lib.rs,則該包下還有一個(gè)同名的庫crate(且src/lib.rs的crate庫)
注:這里lib.rs表示庫文件。滿足上述條件【一個(gè)包至少包含一個(gè)crate,其中可以包含任意個(gè)二進(jìn)制crate、最多只能包含一個(gè)庫crate】
在此,我們有了一個(gè)只包含?src/main.rs?的包,意味著它只含有一個(gè)名為test05的二進(jìn)制 crate。如果一個(gè)包同時(shí)含有?src/main.rs?和?src/lib.rs,則它有兩個(gè) crate:一個(gè)庫和一個(gè)二進(jìn)制項(xiàng),且名字都與包相同。通過將文件放在?src/bin?目錄下,一個(gè)包可以擁有多個(gè)二進(jìn)制 crate:每個(gè)?src/bin?下的文件都會(huì)被編譯成一個(gè)獨(dú)立的二進(jìn)制 crate。
很繞
一個(gè) crate 會(huì)將一個(gè)作用域內(nèi)的相關(guān)功能分組到一起,使得該功能可以很方便地在多個(gè)項(xiàng)目之間共享。舉一個(gè)例子,在第二章使用的?rand
?crate 提供了生成隨機(jī)數(shù)的功能。通過將?rand
?crate 加入到我們項(xiàng)目的作用域中,我們就可以在自己的項(xiàng)目中使用該功能。rand
?crate 提供的所有功能都可以通過該 crate 的名字:rand
?進(jìn)行訪問。
將一個(gè) crate 的功能保持在其自身的作用域中,可以知曉一些特定的功能是在我們的 crate 中定義的還是在?rand
?crate 中定義的,這可以防止?jié)撛诘臎_突。
7.2?定義模塊來控制作用域與私有性
模塊?讓我們可以將一個(gè) crate 中的代碼進(jìn)行分組,以提高可讀性與重用性。模塊還可以控制項(xiàng)的?私有性,即項(xiàng)是可以被外部代碼使用的(public),還是作為一個(gè)內(nèi)部實(shí)現(xiàn)的內(nèi)容,不能被外部代碼使用(private)。
案例:在餐飲業(yè),餐館中會(huì)有一些地方被稱之為?前臺(tái)(front of house),還有另外一些地方被稱之為?后臺(tái)(back of house)。前臺(tái)是招待顧客的地方,在這里,店主可以為顧客安排座位,服務(wù)員接受顧客下單和付款,調(diào)酒師會(huì)制作飲品。后臺(tái)則是由廚師工作的廚房,洗碗工的工作地點(diǎn),以及經(jīng)理做行政工作的地方組成。
我們可以將函數(shù)放置到嵌套的模塊中,來使我們的 crate 結(jié)構(gòu)與實(shí)際的餐廳結(jié)構(gòu)相同。通過執(zhí)行?cargo new --lib restaurant
,來創(chuàng)建一個(gè)新的名為?restaurant
?的庫。
mod front_of_house {
mod hosting {
fn add_to_waitlist() {}
fn seat_at_table() {}
}
mod serving {
fn take_order() {}
fn server_order() {}
fn take_payment() {}
}
}
定義一個(gè)模塊,是以?mod
?關(guān)鍵字為起始,然后指定模塊的名字(本例中叫做?front_of_house
),并且用花括號(hào)包圍模塊的主體。在模塊內(nèi),我們還可以定義其他的模塊,就像本例中的?hosting
?和?serving
?模塊。模塊還可以保存一些定義的其他項(xiàng),比如結(jié)構(gòu)體、枚舉、常量、特性、或者函數(shù)。
src/main.rs
?和?src/lib.rs
?叫做 crate 根。之所以這樣叫它們的原因是,這兩個(gè)文件的內(nèi)容都是一個(gè)從名為?crate
?的模塊作為根的 crate 模塊結(jié)構(gòu),稱為?模塊樹(module tree)。
模塊樹的結(jié)構(gòu)
crate
└── front_of_house
├── hosting
│ ├── add_to_waitlist
│ └── seat_at_table
└── serving
├── take_order
├── serve_order
└── take_payment
這個(gè)樹展示了一些模塊是如何被嵌入到另一個(gè)模塊的(例如,hosting
?嵌套在?front_of_house
?中)。這個(gè)樹還展示了一些模塊是互為?兄弟(siblings) 的,這意味著它們定義在同一模塊中(hosting
?和?serving
?被一起定義在?front_of_house
?中)。繼續(xù)沿用家庭關(guān)系的比喻,如果一個(gè)模塊 A 被包含在模塊 B 中,我們將模塊 A 稱為模塊 B 的?子(child),模塊 B 則是模塊 A 的?父(parent)。注意,整個(gè)模塊樹都植根于名為?crate
?的隱式模塊下。
7.3?路徑用于引用模塊樹中的項(xiàng)
路徑有兩種形式:
-
絕對(duì)路徑(absolute path)從 crate 根開始,以 crate 名或者字面值?
crate
?開頭。 -
相對(duì)路徑(relative path)從當(dāng)前模塊開始,以?
self
、super
?或當(dāng)前模塊的標(biāo)識(shí)符開頭。
絕對(duì)路徑和相對(duì)路徑都后跟一個(gè)或多個(gè)由雙冒號(hào)(::
)分割的標(biāo)識(shí)符。
如何調(diào)用?add_to_waitlist
?函數(shù)?還是同樣的問題,add_to_waitlist
?函數(shù)的路徑是什么?
修改一下src/lib.rs(這個(gè)編譯不成功)
mod front_of_house {
mod hosting {
fn add_to_waitlist() {}
}
}
pub fn eat_at_restaurant() {
// Absolute path
crate::front_of_house::hosting::add_to_waitlist();
// Relative path
front_of_house::hosting::add_to_waitlist();
}
eat_at_restaurant
?函數(shù)是我們 crate 庫的一個(gè)公共API,所以使用?pub
?關(guān)鍵字來標(biāo)記它。
第一種方式,我們?cè)?eat_at_restaurant
?中調(diào)用?add_to_waitlist
?函數(shù),使用的是絕對(duì)路徑。add_to_waitlist
?函數(shù)與?eat_at_restaurant
?被定義在同一 crate 中,這意味著我們可以使用?crate
?關(guān)鍵字為起始的絕對(duì)路徑。
第二種方式,我們?cè)?eat_at_restaurant
?中調(diào)用?add_to_waitlist
,使用的是相對(duì)路徑。這個(gè)路徑以?front_of_house
?為起始,這個(gè)模塊在模塊樹中,與?eat_at_restaurant
?定義在同一層級(jí)。與之等價(jià)的文件系統(tǒng)路徑就是?front_of_house/hosting/add_to_waitlist
。以名稱為起始,意味著該路徑是相對(duì)路徑。
編譯會(huì)報(bào)錯(cuò)
錯(cuò)誤信息說?hosting
?模塊是私有的。換句話說,我們擁有?hosting
?模塊和?add_to_waitlist
?函數(shù)的的正確路徑,但是 Rust 不讓我們使用,因?yàn)樗荒茉L問私有片段。
使用pub關(guān)鍵字暴露路徑
給上述代碼加上pub,依舊報(bào)錯(cuò),還需給函數(shù)也加上,私有性規(guī)則不但應(yīng)用于模塊,還應(yīng)用于結(jié)構(gòu)體、枚舉、函數(shù)和方法。
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
pub fn eat_at_restaurant() {
// Absolute path
crate::front_of_house::hosting::add_to_waitlist();
// Relative path
front_of_house::hosting::add_to_waitlist();
}
在絕對(duì)路徑,我們從?
crate
,也就是 crate 根開始。然后 crate 根中定義了?front_of_house
?模塊。front_of_house
?模塊不是公有的,不過因?yàn)?eat_at_restaurant
?函數(shù)與?front_of_house
?定義于同一模塊中(即,eat_at_restaurant
?和?front_of_house
?是兄弟),我們可以從?eat_at_restaurant
?中引用?front_of_house
。接下來是使用?pub
?標(biāo)記的?hosting
?模塊。我們可以訪問?hosting
?的父模塊,所以可以訪問?hosting
。最后,add_to_waitlist
?函數(shù)被標(biāo)記為?pub
?,我們可以訪問其父模塊,所以這個(gè)函數(shù)調(diào)用是有效的!在相對(duì)路徑,其邏輯與絕對(duì)路徑相同,除了第一步:不同于從 crate 根開始,路徑從?
front_of_house
?開始。front_of_house
?模塊與?eat_at_restaurant
?定義于同一模塊,所以從?eat_at_restaurant
?中開始定義的該模塊相對(duì)路徑是有效的。接下來因?yàn)?hosting
?和?add_to_waitlist
?被標(biāo)記為?pub
,路徑其余的部分也是有效的,因此函數(shù)調(diào)用也是有效的!
使用 super 起始的相對(duì)路徑
還可以使用?super
?開頭來構(gòu)建從父模塊開始的相對(duì)路徑。這么做類似于文件系統(tǒng)中以?..
?開頭的語法。
下面代碼模擬了廚師更正了一個(gè)錯(cuò)誤訂單,并親自將其提供給客戶的情況。
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
pub fn eat_at_restaurant() {
// Absolute path
crate::front_of_house::hosting::add_to_waitlist();
// Relative path
front_of_house::hosting::add_to_waitlist();
}
// 加了這段
fn serve_order() {}
mod back_of_house {
fn fix_incorrect_order() {
cook_order();
super::serve_order();
}
fn cook_order() {}
}
fix_incorrect_order
?函數(shù)在?back_of_house
?模塊中,所以我們可以使用?super
?進(jìn)入?back_of_house
?父模塊,也就是本例中的?crate
?根。在這里,我們可以找到?serve_order
。成功!我們認(rèn)為?back_of_house
?模塊和?server_order
?函數(shù)之間可能具有某種關(guān)聯(lián)關(guān)系,并且,如果我們要重新組織這個(gè) crate 的模塊樹,需要一起移動(dòng)它們。
創(chuàng)建公有的結(jié)構(gòu)體和枚舉
還可以使用?pub
?來設(shè)計(jì)公有的結(jié)構(gòu)體和枚舉,不過有一些額外的細(xì)節(jié)需要注意。如果在一個(gè)結(jié)構(gòu)體定義的前面使用了?pub
?,這個(gè)結(jié)構(gòu)體會(huì)變成公有的,但是這個(gè)結(jié)構(gòu)體的字段仍然是私有的。可以根據(jù)情況決定每個(gè)字段是否公有。
與之相反,如果將枚舉設(shè)為公有,則它的所有成員都將變?yōu)楣?。只需要?enum
?關(guān)鍵字前面加上?pub
7.4?使用use關(guān)鍵字將名稱引入作用域
導(dǎo)入頭文件???
到目前為止,似乎編寫的用于調(diào)用函數(shù)的路徑都很冗長(zhǎng)且重復(fù),并不方便。無論我們選擇?add_to_waitlist
?函數(shù)的絕對(duì)路徑還是相對(duì)路徑,每次想要調(diào)用?add_to_waitlist
?時(shí),都必須指定front_of_house
?和?hosting
。幸運(yùn)的是,有一種方法可以簡(jiǎn)化這個(gè)過程??梢砸淮涡詫⒙窂揭胱饔糜颍缓笫褂?use
?關(guān)鍵字調(diào)用該路徑中的項(xiàng),就如同它們是本地項(xiàng)一樣。
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
// Absolute path
hosting::add_to_waitlist();
// Relative path
hosting::add_to_waitlist();
}
通過?use
?引入作用域的路徑也會(huì)檢查私有性,同其它路徑一樣。
創(chuàng)建慣用的use路徑
要想使用?use
?將函數(shù)的父模塊引入作用域,必須在調(diào)用函數(shù)時(shí)指定父模塊,這樣可以清晰地表明函數(shù)不是在本地定義的,同時(shí)使完整路徑的重復(fù)度最小化。
另一方面,使用?use
?引入結(jié)構(gòu)體、枚舉和其他項(xiàng)時(shí),習(xí)慣是指定它們的完整路徑。
使用as關(guān)鍵字提供新的名稱
使用?use
?將兩個(gè)同名類型引入同一作用域這個(gè)問題還有另一個(gè)解決辦法:在這個(gè)類型的路徑后面,我們使用?as
?指定一個(gè)新的本地名稱或者別名
#![allow(unused_variables)]
fn main() {
use std::fmt::Result;
use std::io::Result as IoResult;
fn function1() -> Result {
// --snip--
Ok(())
}
fn function2() -> IoResult<()> {
// --snip--
Ok(())
}
}
使用外部包
crates.io?上有很多 Rust 社區(qū)成員發(fā)布的包,將其引入你自己的項(xiàng)目都需要一道相同的步驟:在?Cargo.toml?列出它們并通過?use
?將其中定義的項(xiàng)引入項(xiàng)目包的作用域中。
注意標(biāo)準(zhǔn)庫(std
)對(duì)于你的包來說也是外部 crate。因?yàn)闃?biāo)準(zhǔn)庫隨 Rust 語言一同分發(fā),無需修改?Cargo.toml?來引入?std
,不過需要通過?use
?將標(biāo)準(zhǔn)庫中定義的項(xiàng)引入項(xiàng)目包的作用域中來引用它們,比如我們使用的?HashMap
:
#![allow(unused_variables)]
fn main() {
use std::collections::HashMap;
}
嵌套路徑來消除大量的 use 行
use std::cmp::Ordering;
use std::io;
可以寫成
use std::{cmp::Ordering, io};
在較大的程序中,使用嵌套路徑從相同包或模塊中引入很多項(xiàng),可以顯著減少所需的獨(dú)立?use
?語句的數(shù)量!
use std::io;
use std::io::Write;
可以寫成
use std::io::{self, Write};
通過 glob 運(yùn)算符將所有的公有定義引入作用域
如果希望將一個(gè)路徑下?所有?公有項(xiàng)引入作用域,可以指定路徑后跟?*
,glob 運(yùn)算符:
use std::collections::*;
這個(gè)?use
?語句將?std::collections
?中定義的所有公有項(xiàng)引入當(dāng)前作用域。使用 glob 運(yùn)算符時(shí)請(qǐng)多加小心!Glob 會(huì)使得我們難以推導(dǎo)作用域中有什么名稱和它們是在何處定義的。
7.5?將模塊分割進(jìn)不同文件
上述案例中,將?front_of_house
?模塊移動(dòng)到屬于它自己的文件?src/front_of_house.rs?中,通過改變 crate 根文件。crate 根文件是?src/lib.rs,這也同樣適用于以?src/main.rs?為 crate 根文件的二進(jìn)制 crate 項(xiàng)。
文件名: src/lib.rs
mod front_of_house;
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
hosting::add_to_waitlist();
hosting::add_to_waitlist();
}
聲明?front_of_house
?模塊,其內(nèi)容位于?src/front_of_house.rs
pub mod hosting {
pub fn add_to_waitlist() {}
}
接著我們創(chuàng)建一個(gè)?src/front_of_house?目錄和一個(gè)包含?hosting
?模塊定義的?src/front_of_house/hosting.rs?文件:
pub fn add_to_waitlist() {}
模塊樹依然保持相同,eat_at_restaurant
?中的函數(shù)調(diào)用也無需修改繼續(xù)保持有效,即便其定義存在于不同的文件中。這個(gè)技巧讓你可以在模塊代碼增長(zhǎng)時(shí),將它們移動(dòng)到新文件中。
注意,src/lib.rs?中的?pub use crate::front_of_house::hosting
?語句是沒有改變的,在文件作為 crate 的一部分而編譯時(shí),use
?不會(huì)有任何影響。mod
?關(guān)鍵字聲明了模塊,Rust 會(huì)在與模塊同名的文件中查找模塊的代碼。
總結(jié)
Rust 提供了將包組織進(jìn) crate、將 crate 組織進(jìn)模塊,和通過指定絕對(duì)或相對(duì)路徑從一個(gè)模塊引用另一個(gè)模塊中定義的項(xiàng)的方式。你可以通過使用?use
?語句將路徑引入作用域,這樣在多次使用時(shí)可以使用更短的路徑。模塊定義的代碼默認(rèn)是私有的,不過可以選擇增加?pub
?關(guān)鍵字使其定義變?yōu)楣小?mark hidden color="red">文章來源:http://www.zghlxwxcb.cn/news/detail-637234.html
參考:使用包、Crate 和模塊管理不斷增長(zhǎng)的項(xiàng)目 - Rust 程序設(shè)計(jì)語言 簡(jiǎn)體中文版 (bootcss.com)文章來源地址http://www.zghlxwxcb.cn/news/detail-637234.html
到了這里,關(guān)于【Rust】Rust學(xué)習(xí) 第七章使用包、Crate和模塊管理不斷增長(zhǎng)的項(xiàng)目的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!