網站首頁 編程語言 正文
類似es6的模塊化,Rust通過package、create、module來實現代碼的模塊化管理
Rust如何進行模塊化開發?
Rust的代碼組織包括:哪些細節可以暴露,哪些細節是私有的,作用域內哪些名稱有效等等。
- Package(包):Cargo的特性,讓你構建、測試、共享create
- Create(單元包):一個模塊樹,它可以產生一個library或可執行文件
- Module(模塊)、use:讓你控制代碼的組織、作用域、私有路徑
- Path(路徑):為struct、function或module等項命名的方式
Package和Create
create的類型:
- binary(二進制create)
- library(庫create)
其中,關于Create,還有個概念——Create Root:
是源代碼文件Rust編譯器從這里開始,組成你的Create的根Module
一個Package:
- 包含一個Cargo.toml,它描述了如何構建這些Crates
- 只能包含0-1個library create(庫create)
- 可以包含任意數量的binary create(二進制create)
- 但必須至少包含一個create(library或binary)
我們使用cargo新建一個項目
然后會提示: Created binary (application) my-project
package,這代表我們創建了一個二進制的應用程序,名叫my-project
的package
我們進入這個文件夾:
我們可以看到src/min.rs文件,這是我們程序的入口文件,但是我們在Cargo.toml中并沒有看到相關的配置:
[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]
這是因為cargo有一些慣例
Cargo的慣例
- src/main.rs是binary create的create root* create的名與package名相同如果我們還有一個這個文件:src/lib.rs,那么:
- 表明package包含一個library create
- 它是library create的create root
- create的名與package名相同
Cargo將會把create root文件交給rustc(rust編譯器)來構建library或者binary
一個Package可以同時包含src/main.rs和src/lib.rs
一個Package也可以有多個binary create:
- 文件放在src/bin,放在這里的每個文件都是單獨的binary create
Create的作用
將相關功能組合到一個作用域內,便于在項目間進行共享。
同時,這也能防止命名沖突,例如rand create,訪問它的功能需要通過它的名字:rand
定義module來控制作用域和私有性
Module:
- 在一個create內,將代碼進行分組
- 增加可讀性,易于復用
- 控制項目(item)的私有性。public,private
建立module:
- mod關鍵字
- 可嵌套
- 可包含其他項(struct、enum、常量、trait、函數等)的定義
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:
- 這兩個文件(任意一個)的內容形成了名為create的模塊,位于整個模塊樹的根部
- 整個模塊樹在隱式的模塊下
路徑Path
路徑的作用是為了在rust的模塊中找到某個條目
路徑的兩種形式:
- 絕對路徑:從create root開始,使用create名或字面值create
- 相對路徑:從當前模塊開始,使用self(本身),super(上一級)或當前模塊的標識符
路徑至少由一個標識符組成,標識符之間使用::
。
舉個例子(下面這段程序將報錯,我們將在后面講到如何解決):
mod front_of_house {mod hosting {fn add_to_waitlist() {}} } pub fn eat_at_restaurant() {crate::front_of_house::hosting::add_to_waitlist();//絕對路徑front_of_house::hosting::add_to_waitlist();//相對路徑 }
那么為什么會報錯呢?
我們查看報錯的原因:module hosting
is private,編譯器告訴我們,hosting這個module是私有的。至此,為了解決這個問題,我們應該去了解一下私有邊界。
私有邊界(private boundary)
- 模塊不僅可以組織代碼,還可以定義私有邊界
- 如果把函數或struct等設為私有,可以將它放到某個模塊中。
- rust中所有的條目(函數,方法,struct,enum,模塊,常量)默認情況下是私有的
- 父級模塊無法訪問子模塊中的私有條目
- 但是在子模塊中可以使用所有祖先模塊中的條目
為什么rust默認這些條目是私有的呢?因為rust希望能夠隱藏內部的實現細節,這樣就會讓開發者明確知道:更改哪些內部代碼的時候,不會破壞外部的代碼。同時,我們可以使用pub關鍵字將其聲明為公共的。
pub關鍵字
rust默認這些條目為私有的,我們可以使用pub
關鍵字來將某些條目標記為公共的。
我們將hosting
聲明pub,add_to_waitlist
這個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();//絕對路徑front_of_house::hosting::add_to_waitlist();//相對路徑 }
為什么front_of_house
這個mod不需要添加pub呢?因為它們是同級的。
super關鍵字
super:用來訪問父級模塊路徑中的內容,類似文件系統中的..
fn serve_order() {} mod front_of_house {fn fix_incorrect_order() {cook_order();super::serve_order();}fn cook_order() {} }
pub struct
聲明一個公共的struct就是將pub放在struct前:
mod back_of_house {pub struct Breakfast {} }
聲明了一個公共的struct后:
- struct是公共的
- struct的字段默認是私有的
而我們想讓struct中的字段為公有的必須在前面加上pub
:
mod back_of_house {pub struct Breakfast {pub toast: String,//公有的seasonal_fruit: String, //私有的} }
也就是說:struct的字段需要單獨設置pub來變成公有
我們看一個例子:
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);meal.seasonal_fruit = String::from("blueberries");//報錯:field `seasonal_fruit` is private }
pub enum
聲明一個公共的enum就是將pub放在enum前:
mod back_of_house {pub enum Appetizer {} }
我們聲明了一個公共的enum后:
- enum是公共的
- enum的變體也都是公共的
mod back_of_house {pub enum Appetizer {Soup,//公共的Salad, //公共的} }
為什么呢?因為枚舉里面只有變體,只有變體是公共的這個枚舉才有用。而struct中某些部分為私有的也不影響struct的使用,所以rust規定公共的struct中的字段默認為私有的。
Use關鍵字
我們可以使用use
關鍵字將路徑導入到作用域內,而我們引入的東西也任然遵循私有性規則(公共的引入的才能用)
mod front_of_house {pub mod hosting {pub fn add_to_waitlist() {}fn some_function() {}//私有的,使用use導入后,外部依然不能調用這個函數} } use crate::front_of_house::hosting; // 相當于mod hosting {} pub fn eat_at_restaurant() {hosting::add_to_waitlist();hosting::add_to_waitlist();hosting::add_to_waitlist(); }
使用use來指定相對路徑(和使用條目時的規則相同):
use front_of_house::hosting;
我們可以注意到我們調用的add_to_waitlist
是導入的hosting
mod下的,那我們可不可以直接導入function呢?
當然是可以的(不過并不推薦直接導入方法):
mod front_of_house {pub mod hosting {pub fn add_to_waitlist() {}} } use crate::front_of_house::hosting::add_to_waitlist; // 相對于mod hosting {} pub fn eat_at_restaurant() {add_to_waitlist(); }
use的習慣用法
當我們直接導入方法時,我們有可能就搞不清楚是從其他模塊導入的還是在這個作用域下聲明的。
所以,通常情況下,我們導入的通常為父級模塊。
//... use crate::front_of_house::hosting; pub fn eat_at_restaurant() {hosting::add_to_waitlist(); }
不過,struct,enum,其他:指定完整路徑(指定到本身)
use std::collections::HashMap; fn main() {let mut map = HashMap::new();map.insert(1, 2); }
但是同名的條目,我們在引入時需指定父級模塊(比如下面的例子,兩個類型都叫Result)
use std::fmt; use std::io; fn f1() -> fmt::Result {//... } fn f2() -> io::Result {//... } //...
as關鍵字
關于上面同名的問題,還有另一種解決方法:使用as關鍵字
as關鍵字可以為引入的路徑指定本地的別名
use std::fmt::Result; use std::io::Result as IoResult; fn f1() -> Result {//... } fn f2() -> IoResult {//... }
使用 pub use 重新導出名稱
使用 use 將路徑(名稱)導入到作用域內后,該名稱在此作用域內是私有的,外部的模塊是沒辦法訪問use導入的模塊的。
由前面pub的作用可知,類似pub fn、pub mod,我們可以使用pub use
來導入,相當于它導入了這個內容,然后又將它導出了。
(當我們使用pub use時會發現沒有警告:“導入了但沒有使用”,因為它同時也導出了,也被視作使用了這個導入的內容)
導入外部包
我們通過在Cargo.toml中的[dependencies]
添加依賴:
# ... [dependencies] rand = "^0.8.5"
出現:Blocking waiting for file lock on package cache
刪除User/.cargo文件夾中的.package-cache
文件。重新執行cargo build
下載依賴。
很多時候我們的下載速度很慢,我們可以將下載源換到國內,在用戶文件夾下的.cargo
文件夾中添加 config 文件,寫入以下內容:
[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" # 如果所處的環境中不允許使用 git 協議,可以把上面的地址改為 # registry = "https://mirrors.ustc.edu.cn/crates.io-index" #[http] #check-revoke = false
這時候cargo build就會很快了。
我們這樣導入:
use rand::Rng;
另外:標準庫也被當做外部包,需要導入,并且:
- 我們不需要修改Cargo.toml來添加依賴
- 需要使用use將std的特定條目導入到當前作用域 use多次導入(嵌套導入)
use std::{ascii, io}; //相當于:use std::ascii; // use std::io;
這樣的導入該如何簡寫呢?
use std::io; use std::io::Chain;
可以使用self
use std::io::{self, Chain};
如何將模塊放入其他文件?
假如我們的src/lib.rs中的內容是這樣:
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同級目錄下新建front_of_house.rs
,然后將模塊內容寫在文件中:
front_of_house.rs
pub mod hosting {pub fn add_to_waitlist() {} }
lib.rs
mod front_of_house; //...
如果我們想將hosting
模塊的內容單獨存放呢?
我們需要新建一個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;//...
原來的文件內容:
mod front_of_house {pub mod hosting {pub fn add_to_waitlist() {}} }
隨著模塊逐漸變大,這項功能將能夠幫助我們更好的管理代碼
原文鏈接:https://blog.csdn.net/weixin_53312997/article/details/128664006
相關推薦
- 2023-07-16 SVN版本回退 revert to this revision
- 2022-08-08 基于Redis緩存數據常見的三種問題及解決_Redis
- 2022-10-17 C++STL教程之vector模板的使用_C 語言
- 2022-09-22 ansible-playbook 可用參數
- 2022-04-04 webpack-loaders: postcss
- 2023-11-16 當pytorch找不到 CUDA 時,如何修復錯誤:版本 libcublasLt.so.11 未在帶
- 2022-04-14 Python+Tkinter簡單實現注冊登錄功能_python
- 2022-04-24 python自定義封裝帶顏色的logging模塊_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支