Rust如何進(jìn)行模塊化開(kāi)發(fā)技巧分享
類似es6的模塊化,Rust通過(guò)package、create、module來(lái)實(shí)現(xiàn)代碼的模塊化管理
Rust如何進(jìn)行模塊化開(kāi)發(fā)?
Rust的代碼組織包括:哪些細(xì)節(jié)可以暴露,哪些細(xì)節(jié)是私有的,作用域內(nèi)哪些名稱有效等等。
- Package(包):Cargo的特性,讓你構(gòu)建、測(cè)試、共享create
- Create(單元包):一個(gè)模塊樹(shù),它可以產(chǎn)生一個(gè)library或可執(zhí)行文件
- Module(模塊)、use:讓你控制代碼的組織、作用域、私有路徑
- Path(路徑):為struct、function或module等項(xiàng)命名的方式
Package和Create
create的類型:
- binary(二進(jìn)制create)
- library(庫(kù)create)
其中,關(guān)于Create,還有個(gè)概念——Create Root:
是源代碼文件Rust編譯器從這里開(kāi)始,組成你的Create的根Module
一個(gè)Package:
- 包含一個(gè)Cargo.toml,它描述了如何構(gòu)建這些Crates
- 只能包含0-1個(gè)library create(庫(kù)create)
- 可以包含任意數(shù)量的binary create(二進(jìn)制create)
- 但必須至少包含一個(gè)create(library或binary)
我們使用cargo新建一個(gè)項(xiàng)目
然后會(huì)提示: Created binary (application) my-project
package,這代表我們創(chuàng)建了一個(gè)二進(jìn)制的應(yīng)用程序,名叫my-project
的package
我們進(jìn)入這個(gè)文件夾:
我們可以看到src/min.rs文件,這是我們程序的入口文件,但是我們?cè)贑argo.toml中并沒(méi)有看到相關(guān)的配置:
[package] name = "my-project" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies]
這是因?yàn)閏argo有一些慣例
Cargo的慣例
- src/main.rs是binary create的create root* create的名與package名相同如果我們還有一個(gè)這個(gè)文件:src/lib.rs,那么:
- 表明package包含一個(gè)library create
- 它是library create的create root
- create的名與package名相同
Cargo將會(huì)把create root文件交給rustc(rust編譯器)來(lái)構(gòu)建library或者binary
一個(gè)Package可以同時(shí)包含src/main.rs和src/lib.rs
一個(gè)Package也可以有多個(gè)binary create:
- 文件放在src/bin,放在這里的每個(gè)文件都是單獨(dú)的binary create
Create的作用
將相關(guān)功能組合到一個(gè)作用域內(nèi),便于在項(xiàng)目間進(jìn)行共享。
同時(shí),這也能防止命名沖突,例如rand create,訪問(wèn)它的功能需要通過(guò)它的名字:rand
定義module來(lái)控制作用域和私有性
Module:
- 在一個(gè)create內(nèi),將代碼進(jìn)行分組
- 增加可讀性,易于復(fù)用
- 控制項(xiàng)目(item)的私有性。public,private
建立module:
- mod關(guān)鍵字
- 可嵌套
- 可包含其他項(xiàng)(struct、enum、常量、trait、函數(shù)等)的定義
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() {}} }
src/main.rs 和 src/lib.rs 叫做create roots:
- 這兩個(gè)文件(任意一個(gè))的內(nèi)容形成了名為create的模塊,位于整個(gè)模塊樹(shù)的根部
- 整個(gè)模塊樹(shù)在隱式的模塊下
路徑Path
路徑的作用是為了在rust的模塊中找到某個(gè)條目
路徑的兩種形式:
- 絕對(duì)路徑:從create root開(kāi)始,使用create名或字面值create
- 相對(duì)路徑:從當(dāng)前模塊開(kāi)始,使用self(本身),super(上一級(jí))或當(dāng)前模塊的標(biāo)識(shí)符
路徑至少由一個(gè)標(biāo)識(shí)符組成,標(biāo)識(shí)符之間使用::
。
舉個(gè)例子(下面這段程序?qū)?bào)錯(cuò),我們將在后面講到如何解決):
mod front_of_house {mod hosting {fn add_to_waitlist() {}} } pub fn eat_at_restaurant() {crate::front_of_house::hosting::add_to_waitlist();//絕對(duì)路徑front_of_house::hosting::add_to_waitlist();//相對(duì)路徑 }
那么為什么會(huì)報(bào)錯(cuò)呢?
我們查看報(bào)錯(cuò)的原因:module hosting
is private,編譯器告訴我們,hosting這個(gè)module是私有的。至此,為了解決這個(gè)問(wèn)題,我們應(yīng)該去了解一下私有邊界。
私有邊界(private boundary)
- 模塊不僅可以組織代碼,還可以定義私有邊界
- 如果把函數(shù)或struct等設(shè)為私有,可以將它放到某個(gè)模塊中。
- rust中所有的條目(函數(shù),方法,struct,enum,模塊,常量)默認(rèn)情況下是私有的
- 父級(jí)模塊無(wú)法訪問(wèn)子模塊中的私有條目
- 但是在子模塊中可以使用所有祖先模塊中的條目
為什么rust默認(rèn)這些條目是私有的呢?因?yàn)閞ust希望能夠隱藏內(nèi)部的實(shí)現(xiàn)細(xì)節(jié),這樣就會(huì)讓開(kāi)發(fā)者明確知道:更改哪些內(nèi)部代碼的時(shí)候,不會(huì)破壞外部的代碼。同時(shí),我們可以使用pub關(guān)鍵字將其聲明為公共的。
pub關(guān)鍵字
rust默認(rèn)這些條目為私有的,我們可以使用pub
關(guān)鍵字來(lái)將某些條目標(biāo)記為公共的。
我們將hosting
聲明pub,add_to_waitlist
這個(gè)function也要聲明pub
mod front_of_house {pub mod hosting {pub fn add_to_waitlist() {}} } pub fn eat_at_restaurant() {crate::front_of_house::hosting::add_to_waitlist();//絕對(duì)路徑front_of_house::hosting::add_to_waitlist();//相對(duì)路徑 }
為什么front_of_house
這個(gè)mod不需要添加pub呢?因?yàn)樗鼈兪峭?jí)的。
super關(guān)鍵字
super:用來(lái)訪問(wèn)父級(jí)模塊路徑中的內(nèi)容,類似文件系統(tǒng)中的..
fn serve_order() {} mod front_of_house {fn fix_incorrect_order() {cook_order();super::serve_order();}fn cook_order() {} }
pub struct
聲明一個(gè)公共的struct就是將pub放在struct前:
mod back_of_house {pub struct Breakfast {} }
聲明了一個(gè)公共的struct后:
- struct是公共的
- struct的字段默認(rèn)是私有的
而我們想讓struct中的字段為公有的必須在前面加上pub
:
mod back_of_house {pub struct Breakfast {pub toast: String,//公有的seasonal_fruit: String, //私有的} }
也就是說(shuō):struct的字段需要單獨(dú)設(shè)置pub來(lái)變成公有
我們看一個(gè)例子:
mod back_of_house {pub struct Breakfast {pub toast: String,//公有的seasonal_fruit: String, //私有的}impl Breakfast {//一個(gè)關(guān)聯(lián)函數(shù)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);meal.seasonal_fruit = String::from("blueberries");//報(bào)錯(cuò):field `seasonal_fruit` is private }
pub enum
聲明一個(gè)公共的enum就是將pub放在enum前:
mod back_of_house {pub enum Appetizer {} }
我們聲明了一個(gè)公共的enum后:
- enum是公共的
- enum的變體也都是公共的
mod back_of_house {pub enum Appetizer {Soup,//公共的Salad, //公共的} }
為什么呢?因?yàn)槊杜e里面只有變體,只有變體是公共的這個(gè)枚舉才有用。而struct中某些部分為私有的也不影響struct的使用,所以rust規(guī)定公共的struct中的字段默認(rèn)為私有的。
Use關(guān)鍵字
我們可以使用use
關(guān)鍵字將路徑導(dǎo)入到作用域內(nèi),而我們引入的東西也任然遵循私有性規(guī)則(公共的引入的才能用)
mod front_of_house {pub mod hosting {pub fn add_to_waitlist() {}fn some_function() {}//私有的,使用use導(dǎo)入后,外部依然不能調(diào)用這個(gè)函數(shù)} } use crate::front_of_house::hosting; // 相當(dāng)于mod hosting {} pub fn eat_at_restaurant() {hosting::add_to_waitlist();hosting::add_to_waitlist();hosting::add_to_waitlist(); }
使用use來(lái)指定相對(duì)路徑(和使用條目時(shí)的規(guī)則相同):
use front_of_house::hosting;
我們可以注意到我們調(diào)用的add_to_waitlist
是導(dǎo)入的hosting
mod下的,那我們可不可以直接導(dǎo)入function呢?
當(dāng)然是可以的(不過(guò)并不推薦直接導(dǎo)入方法):
mod front_of_house {pub mod hosting {pub fn add_to_waitlist() {}} } use crate::front_of_house::hosting::add_to_waitlist; // 相對(duì)于mod hosting {} pub fn eat_at_restaurant() {add_to_waitlist(); }
use的習(xí)慣用法
當(dāng)我們直接導(dǎo)入方法時(shí),我們有可能就搞不清楚是從其他模塊導(dǎo)入的還是在這個(gè)作用域下聲明的。
所以,通常情況下,我們導(dǎo)入的通常為父級(jí)模塊。
//... use crate::front_of_house::hosting; pub fn eat_at_restaurant() {hosting::add_to_waitlist(); }
不過(guò),struct,enum,其他:指定完整路徑(指定到本身)
use std::collections::HashMap; fn main() {let mut map = HashMap::new();map.insert(1, 2); }
但是同名的條目,我們?cè)谝霑r(shí)需指定父級(jí)模塊(比如下面的例子,兩個(gè)類型都叫Result)
use std::fmt; use std::io; fn f1() -> fmt::Result {//... } fn f2() -> io::Result {//... } //...
as關(guān)鍵字
關(guān)于上面同名的問(wèn)題,還有另一種解決方法:使用as關(guān)鍵字
as關(guān)鍵字可以為引入的路徑指定本地的別名
use std::fmt::Result; use std::io::Result as IoResult; fn f1() -> Result {//... } fn f2() -> IoResult {//... }
使用 pub use 重新導(dǎo)出名稱
使用 use 將路徑(名稱)導(dǎo)入到作用域內(nèi)后,該名稱在此作用域內(nèi)是私有的,外部的模塊是沒(méi)辦法訪問(wèn)use導(dǎo)入的模塊的。
由前面pub的作用可知,類似pub fn、pub mod,我們可以使用pub use
來(lái)導(dǎo)入,相當(dāng)于它導(dǎo)入了這個(gè)內(nèi)容,然后又將它導(dǎo)出了。
(當(dāng)我們使用pub use時(shí)會(huì)發(fā)現(xiàn)沒(méi)有警告:“導(dǎo)入了但沒(méi)有使用”,因?yàn)樗瑫r(shí)也導(dǎo)出了,也被視作使用了這個(gè)導(dǎo)入的內(nèi)容)
導(dǎo)入外部包
我們通過(guò)在Cargo.toml中的[dependencies]
添加依賴:
# ... [dependencies] rand = "^0.8.5"
出現(xiàn):Blocking waiting for file lock on package cache
刪除User/.cargo文件夾中的.package-cache
文件。重新執(zhí)行cargo build
下載依賴。
很多時(shí)候我們的下載速度很慢,我們可以將下載源換到國(guó)內(nèi),在用戶文件夾下的.cargo
文件夾中添加 config 文件,寫(xiě)入以下內(nèi)容:
[source.crates-io] registry = "https://github.com/rust-lang/crates.io-index" replace-with = 'ustc' [source.ustc] registry = "git://mirrors.ustc.edu.cn/crates.io-index" # 如果所處的環(huán)境中不允許使用 git 協(xié)議,可以把上面的地址改為 # registry = "https://mirrors.ustc.edu.cn/crates.io-index" #[http] #check-revoke = false
這時(shí)候cargo build就會(huì)很快了。
我們這樣導(dǎo)入:
use rand::Rng;
另外:標(biāo)準(zhǔn)庫(kù)也被當(dāng)做外部包,需要導(dǎo)入,并且:
- 我們不需要修改Cargo.toml來(lái)添加依賴
- 需要使用use將std的特定條目導(dǎo)入到當(dāng)前作用域 use多次導(dǎo)入(嵌套導(dǎo)入)
use std::{ascii, io}; //相當(dāng)于:use std::ascii; // use std::io;
這樣的導(dǎo)入該如何簡(jiǎn)寫(xiě)呢?
use std::io; use std::io::Chain;
可以使用self
use std::io::{self, Chain};
如何將模塊放入其他文件?
假如我們的src/lib.rs中的內(nèi)容是這樣:
mod front_of_house {pub mod hosting {pub fn add_to_waitlist() {}} } //... mod front_of_house {pub mod hosting {pub fn add_to_waitlist() {}} } //...
我們可以在lib.rs同級(jí)目錄下新建front_of_house.rs
,然后將模塊內(nèi)容寫(xiě)在文件中:
front_of_house.rs
pub mod hosting {pub fn add_to_waitlist() {} }
lib.rs
mod front_of_house; //...
如果我們想將hosting
模塊的內(nèi)容單獨(dú)存放呢?
我們需要新建一個(gè)front_of_house文件夾,并新建hosting.rs文件
hosting.rs
pub fn add_to_waitlist() {}
front_of_house.rs
pub mod hosting;
lib.rs
mod front_of_house;//...
原來(lái)的文件內(nèi)容:
mod front_of_house {pub mod hosting {pub fn add_to_waitlist() {}} }
隨著模塊逐漸變大,這項(xiàng)功能將能夠幫助我們更好的管理代碼
到此這篇關(guān)于Rust如何進(jìn)行模塊化開(kāi)發(fā)的文章就介紹到這了,更多相關(guān)Rust模塊化開(kāi)發(fā)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
rust實(shí)現(xiàn)post小程序(完整代碼)
這篇文章主要介紹了rust實(shí)現(xiàn)一個(gè)post小程序,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-04-04Rust中的Cargo構(gòu)建、運(yùn)行、調(diào)試
Cargo是rustup安裝后自帶的,Cargo?是?Rust?的構(gòu)建系統(tǒng)和包管理器,這篇文章主要介紹了Rust之Cargo構(gòu)建、運(yùn)行、調(diào)試,需要的朋友可以參考下2022-09-09Rust中non_exhaustive的enum使用確保程序健壯性
這篇文章主要為大家介紹了Rust中non_exhaustive的enum使用確保程序健壯性示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11