rust-模塊樹中引用項的路徑

模塊樹中引用項的路徑

為了告訴 Rust 在模塊樹中如何找到某個項,我們使用路徑,就像在文件系統中導航時使用路徑一樣。要調用一個函數,我們需要知道它的路徑。

路徑有兩種形式:

  • 絕對路徑是從 crate 根開始的完整路徑;對于外部 crate 的代碼,絕對路徑以 crate 名稱開頭,對于當前 crate 的代碼,則以字面量 crate 開頭。
  • 相對路徑從當前模塊開始,使用 self、super 或當前模塊中的標識符。

無論是絕對還是相對路徑,都由一個或多個用雙冒號 (::) 分隔的標識符組成。

回到清單 7-1,假設我們想調用 add_to_waitlist 函數。這等同于問:add_to_waitlist 函數的路徑是什么?清單 7-3 包含了刪除了一些模塊和函數后的清單 7-1。

我們將展示兩種方法,從定義在 crate 根的新函數 eat_at_restaurant 中調用 add_to_waitlist 函數。這些路徑都是正確的,但還有另一個問題會導致此示例無法編譯。稍后我們會解釋原因。

eat_at_restaurant 函數是我們的庫 crate 公共 API 的一部分,因此我們用 pub 關鍵字標記它。在“使用 pub 關鍵字暴露路徑”一節中,我們將詳細介紹 pub。

文件名:src/lib.rs

mod front_of_house {mod hosting {fn add_to_waitlist() {}}
}pub fn eat_at_restaurant() {// Absolute pathcrate::front_of_house::hosting::add_to_waitlist();// Relative pathfront_of_house::hosting::add_to_waitlist();
}

清單7-3:使用絕對路徑和相對路徑調用add_to_waitlist函數
第一次在eat_at_restaurant中調用add_to_waitlist函數時,我們使用了絕對路徑。add_to_waitlist函數定義在與eat_at_restaurant相同的crate中,這意味著我們可以使用crate關鍵字來開始一個絕對路徑。然后我們依次包含每個連續的模塊,直到到達add_to_waitlist。你可以想象一個具有相同結構的文件系統:我們會指定路徑/front_of_house/hosting/add_to_waitlist來運行add_to_waitlist程序;使用crate名稱從crate根目錄開始,就像在shell中用/從文件系統根目錄開始一樣。

第二次在eat_at_restaurant中調用add_to_waitlist時,我們使用了相對路徑。該路徑以front_of_house開頭,這是與eat_at_restaurant處于同一級別模塊樹中的模塊名。在這里,文件系統等價物是使用路徑front_of_house/hosting/add_to_waitlist。從模塊名開始意味著該路徑是相對的。

選擇使用相對還是絕對路徑取決于你的項目,并且取決于你更可能將項定義代碼與使用該項的代碼分開移動還是一起移動。例如,如果我們將front_of_house模塊和eat_at_restaurant函數移入名為customer_experience的模塊,則需要更新指向add_to_waitlist的絕對路徑,但相對路徑仍然有效。然而,如果我們將eat_at_restaurant函數單獨移入名為dining的模塊,指向add_to_waitlist調用的絕對路徑保持不變,但需要更新相對路徑。我們的總體偏好是指定絕對路徑,因為更有可能希望獨立地移動代碼定義和項調用。

讓我們嘗試編譯清單7-3,看看為什么它還不能編譯!錯誤信息如清單7-4所示。

$ cargo buildCompiling restaurant v0.1.0 (file:///projects/restaurant)
error[E0603]: module `hosting` is private--> src/lib.rs:9:28|
9 |     crate::front_of_house::hosting::add_to_waitlist();|                            ^^^^^^^  --------------- function `add_to_waitlist` is not publicly re-exported|                            ||                            private module|
note: the module `hosting` is defined here--> src/lib.rs:2:5|
2 |     mod hosting {|     ^^^^^^^^^^^error[E0603]: module `hosting` is private--> src/lib.rs:12:21|
12 |     front_of_house::hosting::add_to_waitlist();|                     ^^^^^^^  --------------- function `add_to_waitlist` is not publicly re-exported|                     ||                     private module|
note: the module `hosting` is defined here--> src/lib.rs:2:5|
2  |     mod hosting {|     ^^^^^^^^^^^For more information about this error, try `rustc --explain E0603`.
error: could not compile `restaurant` (lib) due to 2 previous errors

列表 7-4:構建列表 7-3 中代碼時的編譯器錯誤
錯誤信息顯示模塊 hosting 是私有的。換句話說,我們為 hosting 模塊和 add_to_waitlist 函數指定了正確的路徑,但 Rust 不允許我們使用它們,因為無法訪問私有部分。在 Rust 中,所有項(函數、方法、結構體、枚舉、模塊和常量)默認對父模塊是私有的。如果你想讓某個項如函數或結構體變成私有,可以將其放入一個模塊中。

父模塊中的項不能使用子模塊內的私有項,但子模塊中的項可以使用其祖先模塊中的項。這是因為子模塊封裝并隱藏了它們的實現細節,但子模塊能看到定義它們的上下文。繼續用我們的比喻,隱私規則就像餐廳后臺:那里發生的一切對顧客來說是保密的,但辦公室經理可以看到并操作他們管理的整個餐廳。

Rust 選擇讓模塊系統這樣運作,是為了默認隱藏內部實現細節。這樣,你就知道哪些內部代碼部分可以更改而不會破壞外部代碼。然而,Rust 確實提供了通過 pub 關鍵字將子模塊代碼中的內部部分公開給外層祖先模塊的方法。

用 pub 關鍵字暴露路徑

回到列表 7-4 中提示 hosting 模塊是私有的問題。我們希望父模塊中的 eat_at_restaurant 函數能夠訪問子模塊中的 add_to_waitlist 函數,因此我們在 hosting 模塊前加上 pub 關鍵字,如列表 7-5 所示。

文件名:src/lib.rs

mod front_of_house {pub mod hosting {fn add_to_waitlist() {}}
}// -- snip --

清單7-5:將宿主模塊聲明為pub以便從eat_at_restaurant中使用
不幸的是,清單7-5中的代碼仍然會導致編譯錯誤,如清單7-6所示。

$ cargo buildCompiling restaurant v0.1.0 (file:///projects/restaurant)
error[E0603]: function `add_to_waitlist` is private--> src/lib.rs:10:37|
10 |     crate::front_of_house::hosting::add_to_waitlist();|                                     ^^^^^^^^^^^^^^^ private function|
note: the function `add_to_waitlist` is defined here--> src/lib.rs:3:9|
3  |         fn add_to_waitlist() {}|         ^^^^^^^^^^^^^^^^^^^^error[E0603]: function `add_to_waitlist` is private--> src/lib.rs:13:30|
13 |     front_of_house::hosting::add_to_waitlist();|                              ^^^^^^^^^^^^^^^ private function|
note: the function `add_to_waitlist` is defined here--> src/lib.rs:3:9|
3  |         fn add_to_waitlist() {}|         ^^^^^^^^^^^^^^^^^^^^For more information about this error, try `rustc --explain E0603`.
error: could not compile `restaurant` (lib) due to 2 previous errors

清單 7-6:構建清單 7-5 中代碼時的編譯器錯誤
發生了什么?在 mod hosting 前添加 pub 關鍵字使模塊變為公共。通過此更改,如果我們可以訪問 front_of_house,就能訪問 hosting。但 hosting 的內容仍然是私有的;將模塊設為公共并不會使其內容公開。模塊上的 pub 關鍵字只允許其祖先模塊中的代碼引用它,而不能訪問其內部代碼。因為模塊是容器,僅僅將模塊設為公共作用不大;我們需要進一步選擇將模塊內的一個或多個項也設為公共。

清單 7-6 中的錯誤提示 add_to_waitlist 函數是私有的。隱私規則適用于結構體、枚舉、函數和方法以及模塊。

讓我們通過在定義前添加 pub 關鍵字,使 add_to_waitlist 函數也變成公有,如清單 7-7 所示。

文件名:src/lib.rs

mod front_of_house {  pub mod hosting {  pub fn add_to_waitlist() {}  }  
}  // -- 略 --

清單 7-7:給 mod hosting 和 fn add_to_waitlist 添加 pub 關鍵字后,我們可以從 eat_at_restaurant 調用該函數
現在代碼可以編譯了!為了理解為什么添加 pub 可以讓我們根據隱私規則在 eat_at_restaurant 中使用這些路徑,讓我們看看絕對路徑和相對路徑。

在絕對路徑中,我們以 crate(crate 模塊樹根)開始。front_of_house 模塊定義于 crate 根目錄下。雖然 front_of_house 并非公有,但由于 eat_at_restaurant 與 front_of_house 定義于同一父級(即二者是兄弟關系),所以我們可以從 eat_at_restaurant 引用 front_of_house。接下來是標記為 pub 的 hosting 模塊,因為能訪問到 hosting 的父級,所以可訪問 hosting。最后,add_to_waitlist 函數被標記為 pub,且能訪問其父級,因此該函數調用有效!

相對路徑邏輯與絕對路徑相同,只是在第一步不同:不是從 crate 根開始,而是從 front_of_house 開始。front_of_house 在與 eat_at_restaurant 同一父級中定義,因此以包含 eat_at_restaurant 的那個模塊作為起點的相對路徑有效。而且因為 hosting 和 add_to_waitlist 都被標記為 pub,其余部分也有效,這個函數調用合法!

如果你計劃共享你的庫 crate,以便其他項目使用你的代碼,那么你的公共 API 就是你與用戶之間確定如何交互代碼的契約。在管理公共 API 更改方面,有許多考慮因素,以便他人更容易依賴你的 crate。這些內容超出本書范圍;如果感興趣,請參閱《Rust API 指南》。

同時包含二進制和庫包的最佳實踐

之前提到,一個包既可以包含 src/main.rs(二進制 crate 根),又可以包含 src/lib.rs(庫 crate 根),默認兩者都使用包名作為名稱。這種同時含有庫和二進制 crate 的包通常會在二進制 crate 中只寫足夠啟動可執行程序并調用庫中的代碼,從而讓其他項目能夠利用該包提供的大部分功能,因為庫中的代碼可復用。

應當把模塊樹定義在 src/lib.rs 中,然后任何公有項都能通過以包名開頭的路徑,在二進制 crate 中使用。這樣,二進制 crate 成為了庫 crate 的用戶,就像完全外部的另一個crate一樣,只能使用公有 API。這幫助你設計良好的 API——不僅你是作者,同時也是客戶!

第12章中,我們將演示這種組織方式,通過一個命令行程序,該程序既包含二進制crate,也包含庫crate。
以 super 開頭的相對路徑
我們可以通過在路徑開頭使用 super 來構造從父模塊開始的相對路徑,而不是當前模塊或 crate 根。這樣類似于文件系統中以 … 語法開頭的路徑。使用 super 可以引用我們知道位于父模塊中的項,這使得當模塊與父模塊關系密切但父模塊將來可能會被移動到其他位置時,重新組織模塊樹更加方便。

考慮清單 7-8 中的代碼,它模擬了廚師修正錯誤訂單并親自送給顧客的情景。在 back_of_house 模塊中定義的 fix_incorrect_order 函數通過指定以 super 開頭的 deliver_order 路徑調用了定義在父模塊中的 deliver_order 函數。

文件名:src/lib.rs

fn deliver_order() {}mod back_of_house {fn fix_incorrect_order() {cook_order();super::deliver_order();}fn cook_order() {}
}

清單 7-8:使用以 super 開頭的相對路徑調用函數
fix_incorrect_order 函數位于 back_of_house 模塊,因此我們可以用 super 返回到 back_of_house 的父模塊,在這里是 crate 根。從那里查找 deliver_order 并找到它。成功!我們認為 back_of_house 模塊和 deliver_order 函數很可能保持這種關系,并且如果決定重組 crate 的模塊樹,它們會一起被移動。因此,我們用了 super,這樣如果這段代碼以后移到別的模塊,只需更新更少的位置。

讓結構體和枚舉公開

我們也可以用 pub 將結構體和枚舉設為公開,但對于結構體和枚舉來說,pub 的用法有一些額外細節。如果在結構體定義前加上 pub,則該結構體是公開的,但其字段仍然是私有的。字段是否公開,可以逐個設置。在清單 7-9 中,我們定義了一個公共的 back_of_house::Breakfast 結構體,其中 toast 字段是公有,而 seasonal_fruit 字段是私有。這模擬餐廳里顧客能選擇配餐面包種類,但廚師根據季節庫存決定搭配水果這一情況。可選水果變化快,所以顧客既不能選擇,也看不到具體是什么水果。

文件名:src/lib.rs

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() {// 夏天點一份帶黑麥吐司(Rye)的早餐。let mut meal = back_of_house::Breakfast::summer("Rye");// 改變想要面包種類。meal.toast = String::from("Wheat");println!("我想要 {} 吐司,謝謝", meal.toast);// 如果取消注釋下一行,將無法編譯;因為不允許訪問或修改隨餐附帶季節性水果。// meal.seasonal_fruit = String::from("blueberries");
}

清單 7-9:部分字段公有、部分字段私有的結構體
由于 back_of_house::Breakfast 的 toast 字段是公有,在 eat_at_restaurant 中可以通過點號訪問讀寫該字段。但不能訪問 seasonal_fruit,因為它是私有字段。嘗試取消注釋修改 seasonal_fruit 那行代碼,會看到編譯錯誤!

另外,由于 BackOfHouse 有私有字段,該結構必須提供一個公共關聯函數用于創建實例(此處命名為 summer)。否則,在 eat_at_restaurant 無法創建 Breakfast 實例,因為無法設置 private 字段值。

相比之下,如果將枚舉設為 public,那么所有變體都是 public,只需在 enum 前加 pub,如清單 7-10 所示:

文件名:src/lib.rs

mod back_of_house {pub enum Appetizer {Soup,Salad,}
}pub fn eat_at_restaurant() {let order1 = back_of_house::Appetizer::Soup;let order2 = back_of_house::Appetizer::Salad;
}

清單 7-10:將枚舉聲明為 public 會使所有變體都成為 public
因為 Appetizer 枚舉被聲明為 public,所以在 eat_at_restaurant 中能夠使用 Soup 和 Salad 兩個變體。

除非其變體也是公有,否則枚舉沒什么用;每次都給所有變體現加上 pub 很麻煩,因此默認情況下,enum 的所有變體現均為公有。而 struct 通常即使沒有公開其字段也很實用,所以 struct 字段默認全部私有,除非顯式標記為 pub。

還有一種涉及 pub 的情況尚未介紹,那就是最后一個關于模組系統特性的 use 關鍵字。接下來先講解 use 本身,然后再展示如何結合使用 pub 和 use 。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/917078.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/917078.shtml
英文地址,請注明出處:http://en.pswp.cn/news/917078.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

mac n切換node版本報錯Bad CPU type in executable

該node版本僅支持intel芯片,不支持Apple 芯片(M1/M2/M3/M4),所以需要下載Rosetta 2 ,讓node可以在搭載 Apple 芯片的 Mac 上運行。 env: node: Bad CPU type in executable /opt/homebrew/bin/n: line 753: /usr/local…

經典算法之美:冒泡排序的優雅實現

經典算法之美:冒泡排序的優雅實現基本概念工作原理介紹具體實現代碼實現總結基本概念 冒泡排序是一種簡單的排序算法,通過重復比較相鄰的元素并交換它們的位置來實現排序。它的名稱來源于較小的元素像氣泡一樣逐漸“浮”到數組的頂端。 工作原理 介紹…

click和touch事件觸發順序 糊里糊涂解決的奇怪bug

問題詳情 在嵌入式硬件設備里,測試 “點擊input密碼框,彈出第三方自帶鍵盤,點擊密碼框旁的小眼睛,切換輸入內容加密狀態,鍵盤收起/彈出狀態不變” 的功能邏輯;實際情況卻是 “點擊鍵盤或input框之外的任何地…

【0基礎PS】Photoshop (PS) 理論知識

目錄前言一、Photoshop 核心概念與定位?二、圖像基礎理論?三、圖層理論:PS 的核心工作機制?四、選區與蒙版?五、調色核心理論?六、常用文件格式?學習建議?總結前言 在數字圖像編輯領域,Photoshop(簡稱 PS)無疑是行業標桿級…

數據庫 設計 pdm comment列表顯示和生成建表sql

按如下步驟 生成見建表語句 comment非空使用comment 生成字段注釋, 空的時候使用name 生成字段注釋 sql腳本模板編輯 參考 PowerDesigner生成mysql字段comment 注釋-騰訊云開發者社區-騰訊云 版本不同這邊的設置不同 這個勾打上

嵌入式基礎知識復習(C語言)

知識擴展7.28 嵌入式產品特點、開發環境、計算機組成、Linux終端初識1、嵌入式產品。特點:低功耗、根據用戶需求定制。硬件:arm處理器。軟件:Linux操作系統arm架構:精簡指令集、低功耗(移動/嵌入式)。 …

LeetCode Hot 100 尋找兩個正序數組的中位數

給定兩個大小分別為 m 和 n 的正序(從小到大)數組 nums1 和 nums2。請你找出并返回這兩個正序數組的 中位數 。算法的時間復雜度應該為 O(log (mn)) 。示例 1:輸入:nums1 [1,3], nums2 [2] 輸出:2.00000 解釋&#x…

監控場景視頻質量異常修復:陌訊動態增強算法實戰解析

原創聲明:本文為原創技術解析,核心技術參數與架構引用自《陌訊技術白皮書》,禁止未經授權轉載。一、行業痛點:視頻質量異常的連鎖難題在安防監控、智慧交通等場景中,視頻質量異常已成為 AI 分析的主要瓶頸。據行業報告…

一個簡單的mvvm示例與數據雙向綁定

這就是一個簡單的數據雙向綁定的demo,參考即可(cmakelist我沒按他的寫,但是大差不差) 目錄 1.示例demo File: CMakeLists.txt File: main.cpp File: model/physiologymodel.cpp File: viewmodel/physiologyviewmodel.h Fil…

哈希的概念及其應用

哈希的概念及其應用哈希概念常見的哈希其他哈希字符串哈希(算法競賽常用)字符串哈希OJP3370 【模板】字符串哈希 - 洛谷P10468 兔子與兔子 - 洛谷哈希沖突哈希函數設計原則哈希沖突解決方法—閉散列閉散列的線性探測閉散列的二次探測哈希沖突解決方法—開…

【分布式的個人博客部署】

綜合項目-搭建個人博客一、運行環境二、基礎配置三、業務需求第一步:準備工作1、配置靜態IP2、修改hosts映射3、開啟防火墻4、時間同步5、配置免密ssh登錄第二步:環境搭建1、Server-web端安裝LNMP環境軟件2、Server-NFS-DNS端上傳博客軟件3、Server-NFS-…

藍橋杯----DS18B20溫度傳感器

(二)、溫度傳感器1、One-Wire總線One-Wire總線利用一根線實現雙向通信。因此其協議對時序的要求較嚴格,如應答等時序都有明確的時間要求。基本的時序包括復位及應答時序、寫一位時序讀一位時序。單總線即只有一根數據線,系統中的數…

科技賦能成長 腦力啟迪未來

——西安臻昊科技與秦嶺云數智共筑腦科學教育新生態 2025年6月26日,西安臻昊科技(集團)有限責任公司與秦嶺云數智(陜西)科技有限公司正式簽署腦象評測技術戰略合作協議,雙方將依托技術互補與資源協同&#…

Docker部署的PostgreSQL慢查詢日志配置指南

目錄 1. 核心步驟 1.1 修改配置文件 1.2 動態加載配置(無需重啟容器) 1.3 驗證配置生效 1.3.1 查看參數 1.3.2 執行測試慢查詢 2. 高級用法 2.1 使用分析工具 2.2 啟用擴展 3. 注意事項 3.1 日志目錄權限 3.2 性能影響 配置Docker部署的Pos…

C# 入門教程(四)委托詳解

文章目錄1、什么是委托2、委托的聲明(自定義委托)3、委托的使用3.1 實例:把方法當作參數傳給另一個方法3.2 注意:難精通易使用功能強大東西,一旦被濫用則后果非常嚴重4、委托的高級使用4.1 多播(multicast)委托4.2隱式…

React的基本語法和原理

3. React條件渲染某些情況下,姐妹的內容會根據不同的情況顯示不同的內容,或者決定是否渲染某部分內容: 在React中,所有的條件判斷和普通的JavaScript代碼一致;常見的條件渲染的方式有哪些?方式一&#xff1…

如何在 Gradle 項目中添加依賴?(以添加 AndroidX 版本的 RecyclerView 為例)

1. 確保項目已啟用 AndroidX RecyclerView 的現代版本屬于 AndroidX 庫,需確保項目已啟用 AndroidX: 在 gradle.properties 中應有以下配置(通常新建項目默認開啟):android.useAndroidXtrue android.enableJetifiert…

深度學習與圖像處理 | 基于PaddlePaddle的梯度下降算法實現(線性回歸投資預測)

演示基于PaddlePaddle自動求導技術實現梯度下降,簡化求解過程。01、梯度下降法梯度下降法是機器學習領域非常重要和具有代表性的算法,它通過迭代計算來逐步尋找目標函數極小值。既然是一種迭代計算方法,那么最重要的就是往哪個方向迭代&#…

負載均衡集群HAproxy

HAProxy 簡介HAProxy 是一款高性能的負載均衡器和代理服務器,支持 TCP 和 HTTP 應用。廣泛用于高可用性集群,能夠有效分發流量到多個后端服務器,確保服務的穩定性和可擴展性。HAProxy 核心功能負載均衡:支持輪詢(round…

重生之我在10天內卷贏C++ - DAY 1

坐穩了,我們的C重生之旅現在正式發車!請系好安全帶,前方高能,但絕對有趣!🚀 重生之我在10天內卷贏C - DAY 1導師寄語:嘿,未來的編程大神!歡迎來到C的世界。我知道&#x…