30天拿下Rust之箱、包和模塊

希望睿智 2024-05-19 07:10:02
概述

Rust語言使用模塊系統來組織工程和代碼。模塊系統允許我們將相關的函數、類型、常量等組織在一起,形成一個邏輯上的單元。通過模塊系統,我們可以隱藏實現細節,只暴露必要的接口,從而提高代碼的可讀性和可維護性。Rust的模塊系統還支持路徑依賴和重導出等功能,使得代碼的組織更加靈活和方便。

Rust的模塊系統中有三個非常重要的概念,分別是:箱(Crate)、包(Package)和模塊(Module),下面逐一進行介紹。

箱(Crate)

箱,英文爲Crate,是Rust中的編譯單元和構建單元,也是Cargo打包和分發的基本單位。Crate可以是庫(library crate),也可以是二進制程序(binary crate)。庫crate包含了可以被其他crate使用的代碼,二進制crate則包含了可以執行的程序。每個crate都有一個crate root,它是編譯器開始構建crate模塊樹的源文件。對于庫crate,crate root通常是src/lib.rs文件;對于二進制crate,crate root通常是src/main.rs文件。

通過crate,我們可以將代碼進一步拆分成更小的、更易于管理和維護的單元。當在Cargo中創建一個新的項目時,實際上就是在創建一個Crate。通過cargo new my_crate命令,Cargo將爲我們初始化一個新的Crate結構,其中包括:源碼目錄、測試文件、Cargo.toml配置文件等。在Rust中,Crate是編譯時的概念,它指代的是編譯後生成的一個單元,可以是一個庫或者一個可執行程序。

包(Package)

包,英文爲Package,是Cargo用于組織和構建代碼的基本單位。每個Rust項目都包含至少一個Package,並通過名爲Cargo.toml的配置文件來描述其屬性和依賴關系。Package的元數據存儲在Cargo.toml文件中,這個文件包含了關于Package的基本信息,比如:名稱、版本、作者、描述、許可證等。另外,Cargo.toml還列出了Package的依賴項,這些依賴項是其他Packages或Crates,它們會被Cargo自動下載和構建。

Package通常包含源碼目錄,包括但不限于src目錄下的main.rs或lib.rs。如果項目更複雜,還可以有多個模塊文件和子模塊文件夾。一個Package可以包含一個或多個Crates,但通常情況下,一個簡單的Package會對應一個單一的Crate。當通過cargo build命令構建項目時,最終輸出的二進制文件或庫文件就是這個Crate。

模塊(Module)

模塊,英文爲Module,是用于在crate內部進行分層和封裝的機制。模塊內部又可以包含模塊,從而形成一個樹形結構,也稱爲模塊樹。每個crate會自動産生一個與當前crate同名的模塊,作爲這個樹形結構的根節點。模塊是元素(比如:函數、結構體、trait等)的集合,是一種抽象的概念,而文件則是承載這個概念的實體。

在Rust中,創建新模塊主要有以下三種方式。

1、在一個文件中創建內嵌模塊。這可以通過直接使用mod關鍵字來實現,模塊的內容會被包含在大括號內部。

2、獨立的一個文件就是一個模塊,文件名即是模塊名。

3、一個文件夾也可以代表一個模塊。在這種情況下,有兩種方法可以實現:

(1)文件夾內部需要有一個名爲mod.rs的文件,這個文件就是這個模塊的入口。在rustc 1.30版本之前,這是唯一的方法。

(2)在文件夾同級目錄裏創建一個與模塊(文件夾)同名的rs文件。在rustc 1.30版本之後,更建議使用這樣的命名方式,以避免項目中存在大量同名的mod.rs文件。

模塊樹

模塊樹是一個邏輯上的分層結構,它反映了源代碼文件的組織方式。每個Rust項目都可以看作一個模塊樹的根,其中包含零個或多個子模塊。每個模塊可以進一步包含其他的子模塊,從而形成嵌套的層次結構。

在下面的示例模塊樹中,lib.rs是crate的根模塊,shapes和math是它的子模塊。circle和rectangle是shapes的子模塊,algebra和geometry是math的子模塊。shapes之所以是模塊,是因爲shapes文件夾下有一個mod.rs文件。math之所以是模塊,是因爲math同級目錄下有一個同名的math.rs文件。在後面內容的介紹當中,我們也會用到這裏的示例模塊樹。

project/├── src/│   ├── lib.rs             // crate根模塊│   ├── shapes/│   │   ├── mod.rs         // shapes模塊│   │   ├── circle.rs│   │   └── rectangle.rs│   ├── math/│   │   ├── algebra.rs│   │   └── geometry.rs│   └── math.rs           // math模塊

模塊路徑

在Rust中,模塊路徑是用于唯一標識模塊中定義的元素(比如:函數、結構體等)的字符串。模塊路徑由一系列由雙冒號(::)分隔的標識符組成,從crate根開始,一直到指定的項,可以是絕對路徑或相對路徑。

絕對路徑:以crate::開始,表示從crate根開始的完整路徑。在下面的示例代碼中,crate::shapes::circle::Area表示從crate根開始的shapes子模塊、circle子目錄的Area函數。

use crate::shapes::circle::Area;

相對路徑:直接使用模塊名稱表示同級模塊,或者相對于當前模塊的子模塊。有兩個特殊的標識需要記住,self::表示當前模塊,super::表示當前模塊的父模塊。

// 在shapes/mod.rs中引用circle.rs中的內容use self::circle::Area;// 在circle.rs中引用shapes/mod.rs中定義的公共常量DEFAULT_RADIUSuse super::DEFAULT_RADIUS;// 在同一目錄下引用rectangle模塊use rectangle::Rectangle;

訪問權限

在Rust中,訪問權限是通過pub關鍵字來控制的。默認情況下,如果不加修飾符,模塊中的成員訪問權將是私有的。這意味著,它們只能在定義它們的模塊內部被訪問。如果想讓其他模塊能夠訪問某個成員,就需要在該模塊和該成員前加上pub關鍵字來聲明其爲公開的。

訪問權限主要有兩種:一種是模塊級的訪問權限,另一種是成員級別的訪問權限。

1、模塊級的訪問權限。公開模塊可以在任何地方被訪問,只要我們知道正確的路徑。私有模塊只能在與其平級的位置,或下級的位置被訪問。也就是說,如果一個模塊是私有的,那麽只有在其同級模塊或子模塊中才能引用它。

2、成員級別的訪問權限。使用pub關鍵字標記的成員是公開的,可以在其他模塊中通過路徑來訪問。沒有使用pub關鍵字標記的成員是私有的,只能在定義它們的模塊內部訪問。

// 公開模塊pub mod public_module {    // 公開函數,可以在其他模塊中訪問    pub fn public_function() {    }      // 私有函數,只能在本模塊內部訪問    fn private_function() {    }}// 私有模塊mod private_module {    // 這個模塊是私有的,不能在其他模塊中直接訪問    fn private_function() {    }}fn main() {    public_module::public_function();}

除此之外,Rust還提供了更細粒度的訪問控制,允許我們指定一個成員僅在crate內部可見,或者僅在特定的模塊及其子模塊中可見。pub(crate)表示該成員在當前crate的任何地方都可見,但在外部crate中不可見。pub(in module)表示該成員在指定的模塊及其子模塊中可見,在其他模塊不可見。

// 函數僅在當前crate內可見pub(crate) fn crate_function() {}// 公開模塊pub mod my_module {    // 函數僅在當前模塊及其子模塊中可見    pub(in crate::my_module) fn module_function() {    }    pub fn public_function() {        // 可以調用crate_function        crate::crate_function();        // 可以調用module_function        module_function();    }}  // 另一個模塊mod another_module {    pub fn another_function() {        crate::crate_function();        // 下面的代碼會提示編譯錯誤:function `module_function` is private        super::my_module::module_function();    }}fn main() {    crate_function();}

如果模塊中定義了結構體,那麽結構體本身以及它的字段默認都是私有的。如果希望結構體的某個字段能夠被外部訪問,則需要在結構體和該字段前均加上pub關鍵字。枚舉類型則不同,只需要在枚舉類型前加上pub關鍵字,而不需要在枚舉成員前加上pub關鍵字。

使用use

use關鍵字用于導入模塊或庫中的元素(比如:函數、結構體等),以便在當前作用域中使用它們而無需使用完全限定的名稱。use語句通常放在文件的頂部,緊接在模塊聲明之後。

use關鍵字的使用方式主要以下幾種。

1、導入整個模塊。可以使用use來導入整個模塊,這樣我們就可以直接使用該模塊中公開的成員。

// 導入std模塊中的vec模塊use std::vec;fn main() {    // 直接使用vec!宏    let value = vec![1, 2, 3];    println!("{:?}", value);}

2、導入特定項。可以使用use來導入模塊中的特定項,而不是整個模塊。

// 只導入HashMapuse std::collections::HashMap;

3、重命名導入的項。如果導入的元素在當前作用域中已經存在同名項,或者想要使用不同的名稱來引用它,我們可以使用as關鍵字來重命名。

// 重命名HashMap爲:MyMapuse std::collections::HashMap as MyMap;

4、使用通配符導入。使用*可以導入模塊中所有公開的成員,但需要注意的是,過度使用通配符導入可能會導致名稱沖突和不可預見的行爲,因此通常建議明確導入你需要的元素。

// 導入std::collections模塊中的所有公開成員use std::collections::*;

5、多個use語句可以組合在一起,以提高便捷性和可讀性。

use std::{    fs::File,    io::{self, Write},};

總結

Rust的模塊系統是其代碼組織管理的核心部分,它提供了一種方式來封裝和組織代碼,控制作用域和路徑的私有性,以及導出公共接口。模塊系統使得開發者能夠構建大型、複雜的應用程序,同時保持代碼的清晰性和可維護性。

0 阅读:0

希望睿智

簡介:軟件技術分享,一起學習,一起成長,一起進步