W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
ch07-04-bringing-paths-into-scope-with-the-use-keyword.md
commit 2d605f66b3d891dea0a2f684ece508aa2282b854
到目前為止,似乎我們編寫的用于調(diào)用函數(shù)的路徑都很冗長且重復(fù),并不方便。例如,示例 7-7 中,無論我們選擇 add_to_waitlist
函數(shù)的絕對路徑還是相對路徑,每次我們想要調(diào)用 add_to_waitlist
時(shí),都必須指定front_of_house
和 hosting
。幸運(yùn)的是,有一種方法可以簡化這個(gè)過程。我們可以使用 use
關(guān)鍵字將路徑一次性引入作用域,然后調(diào)用該路徑中的項(xiàng),就如同它們是本地項(xiàng)一樣。
在示例 7-11 中,我們將 crate::front_of_house::hosting
模塊引入了 eat_at_restaurant
函數(shù)的作用域,而我們只需要指定 hosting::add_to_waitlist
即可在 eat_at_restaurant
中調(diào)用 add_to_waitlist
函數(shù)。
文件名: src/lib.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
hosting::add_to_waitlist();
hosting::add_to_waitlist();
}
示例 7-11: 使用 ?use
?將模塊引入作用域
在作用域中增加 use
和路徑類似于在文件系統(tǒng)中創(chuàng)建軟連接(符號連接,symbolic link)。通過在 crate 根增加 use crate::front_of_house::hosting
,現(xiàn)在 hosting
在作用域中就是有效的名稱了,如同 hosting
模塊被定義于 crate 根一樣。通過 use
引入作用域的路徑也會檢查私有性,同其它路徑一樣。
你還可以使用 use
和相對路徑來將一個(gè)項(xiàng)引入作用域。示例 7-12 展示了如何指定相對路徑來取得與示例 7-11 中一樣的行為。
文件名: src/lib.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use self::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
hosting::add_to_waitlist();
hosting::add_to_waitlist();
}
示例 7-12: 使用 ?use
?和相對路徑將模塊引入作用域
在示例 7-11 中,你可能會比較疑惑,為什么我們是指定 use crate::front_of_house::hosting
,然后在 eat_at_restaurant
中調(diào)用 hosting::add_to_waitlist
,而不是通過指定一直到 add_to_waitlist
函數(shù)的 use
路徑來得到相同的結(jié)果,如示例 7-13 所示。
文件名: src/lib.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting::add_to_waitlist;
pub fn eat_at_restaurant() {
add_to_waitlist();
add_to_waitlist();
add_to_waitlist();
}
示例 7-13: 使用 ?use
?將 ?add_to_waitlist
?函數(shù)引入作用域,這并不符合習(xí)慣
雖然示例 7-11 和 7-13 都完成了相同的任務(wù),但示例 7-11 是使用 use
將函數(shù)引入作用域的習(xí)慣用法。要想使用 use
將函數(shù)的父模塊引入作用域,我們必須在調(diào)用函數(shù)時(shí)指定父模塊,這樣可以清晰地表明函數(shù)不是在本地定義的,同時(shí)使完整路徑的重復(fù)度最小化。示例 7-13 中的代碼不清楚 add_to_waitlist
是在哪里被定義的。
另一方面,使用 use
引入結(jié)構(gòu)體、枚舉和其他項(xiàng)時(shí),習(xí)慣是指定它們的完整路徑。示例 7-14 展示了將 HashMap
結(jié)構(gòu)體引入二進(jìn)制 crate 作用域的習(xí)慣用法。
文件名: src/main.rs
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert(1, 2);
}
示例 7-14: 將 ?HashMap
?引入作用域的習(xí)慣用法
這種習(xí)慣用法背后沒有什么硬性要求:它只是一種慣例,人們已經(jīng)習(xí)慣了以這種方式閱讀和編寫 Rust 代碼。
這個(gè)習(xí)慣用法有一個(gè)例外,那就是我們想使用 use
語句將兩個(gè)具有相同名稱的項(xiàng)帶入作用域,因?yàn)?Rust 不允許這樣做。示例 7-15 展示了如何將兩個(gè)具有相同名稱但不同父模塊的 Result
類型引入作用域,以及如何引用它們。
文件名: src/lib.rs
use std::fmt;
use std::io;
fn function1() -> fmt::Result {
// --snip--
}
fn function2() -> io::Result<()> {
// --snip--
}
示例 7-15: 使用父模塊將兩個(gè)具有相同名稱的類型引入同一作用域
如你所見,使用父模塊可以區(qū)分這兩個(gè) Result
類型。如果我們是指定 use std::fmt::Result
和 use std::io::Result
,我們將在同一作用域擁有了兩個(gè) Result
類型,當(dāng)我們使用 Result
時(shí),Rust 則不知道我們要用的是哪個(gè)。
使用 use
將兩個(gè)同名類型引入同一作用域這個(gè)問題還有另一個(gè)解決辦法:在這個(gè)類型的路徑后面,我們使用 as
指定一個(gè)新的本地名稱或者別名。示例 7-16 展示了另一個(gè)編寫示例 7-15 中代碼的方法,通過 as
重命名其中一個(gè) Result
類型。
文件名: src/lib.rs
use std::fmt::Result;
use std::io::Result as IoResult;
fn function1() -> Result {
// --snip--
}
fn function2() -> IoResult<()> {
// --snip--
}
示例 7-16: 使用 ?as
? 關(guān)鍵字重命名引入作用域的類型
在第二個(gè) use
語句中,我們選擇 IoResult
作為 std::io::Result
的新名稱,它與從 std::fmt
引入作用域的 Result
并不沖突。示例 7-15 和示例 7-16 都是慣用的,如何選擇都取決于你!
使用 use
關(guān)鍵字,將某個(gè)名稱導(dǎo)入當(dāng)前作用域后,這個(gè)名稱在此作用域中就可以使用了,但它對此作用域之外還是私有的。如果想讓其他人調(diào)用我們的代碼時(shí),也能夠正常使用這個(gè)名稱,就好像它本來就在當(dāng)前作用域一樣,那我們可以將 pub
和 use
合起來使用。這種技術(shù)被稱為 “重導(dǎo)出(re-exporting)”:我們不僅將一個(gè)名稱導(dǎo)入了當(dāng)前作用域,還允許別人把它導(dǎo)入他們自己的作用域。
示例 7-17 將示例 7-11 根模塊中的 use
改為 pub use
。
文件名: src/lib.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
hosting::add_to_waitlist();
hosting::add_to_waitlist();
}
示例 7-17: 通過 pub use
使名稱可從新作用域中被導(dǎo)入至任何代碼
通過 pub use
重導(dǎo)出,外部代碼現(xiàn)在可以通過新路徑 restaurant::hosting::add_to_waitlist
來調(diào)用 add_to_waitlist
函數(shù)。如果沒有指定 pub use
,外部代碼需在其作用域中調(diào)用 restaurant::front_of_house::hosting::add_to_waitlist
。
當(dāng)你代碼的內(nèi)部結(jié)構(gòu)與調(diào)用你代碼的程序員所想象的結(jié)構(gòu)不同時(shí),重導(dǎo)出會很有用。例如,在這個(gè)餐館的比喻中,經(jīng)營餐館的人會想到“前臺”和“后臺”。但顧客在光顧一家餐館時(shí),可能不會以這些術(shù)語來考慮餐館的各個(gè)部分。使用 pub use
,我們可以使用一種結(jié)構(gòu)編寫代碼,卻將不同的結(jié)構(gòu)形式暴露出來。這樣做使我們的庫井井有條,也使開發(fā)這個(gè)庫的程序員和調(diào)用這個(gè)庫的程序員都更加方便。
在第二章中我們編寫了一個(gè)猜猜看游戲。那個(gè)項(xiàng)目使用了一個(gè)外部包,rand
,來生成隨機(jī)數(shù)。為了在項(xiàng)目中使用 rand
,在 Cargo.toml 中加入了如下行:
文件名: Cargo.toml
rand = "0.8.3"
在 Cargo.toml 中加入 rand
依賴告訴了 Cargo 要從 crates.io 下載 rand
和其依賴,并使其可在項(xiàng)目代碼中使用。
接著,為了將 rand
定義引入項(xiàng)目包的作用域,我們加入一行 use
起始的包名,它以 rand
包名開頭并列出了需要引入作用域的項(xiàng)?;貞浺幌碌诙碌?“生成一個(gè)隨機(jī)數(shù)” 部分,我們曾將 Rng
trait 引入作用域并調(diào)用了 rand::thread_rng
函數(shù):
use rand::Rng;
fn main() {
let secret_number = rand::thread_rng().gen_range(1..=100);
}
crates.io 上有很多 Rust 社區(qū)成員發(fā)布的包,將其引入你自己的項(xiàng)目都需要一道相同的步驟:在 Cargo.toml 列出它們并通過 use
將其中定義的項(xiàng)引入項(xiàng)目包的作用域中。
注意標(biāo)準(zhǔn)庫(std
)對于你的包來說也是外部 crate。因?yàn)闃?biāo)準(zhǔn)庫隨 Rust 語言一同分發(fā),無需修改 Cargo.toml 來引入 std
,不過需要通過 use
將標(biāo)準(zhǔn)庫中定義的項(xiàng)引入項(xiàng)目包的作用域中來引用它們,比如我們使用的 HashMap
:
use std::collections::HashMap;
這是一個(gè)以標(biāo)準(zhǔn)庫 crate 名 std
開頭的絕對路徑。
當(dāng)需要引入很多定義于相同包或相同模塊的項(xiàng)時(shí),為每一項(xiàng)單獨(dú)列出一行會占用源碼很大的空間。例如猜猜看章節(jié)示例 2-4 中有兩行 use
語句都從 std
引入項(xiàng)到作用域:
文件名: src/main.rs
// --snip--
use std::cmp::Ordering;
use std::io;
// --snip--
相反,我們可以使用嵌套路徑將相同的項(xiàng)在一行中引入作用域。這么做需要指定路徑的相同部分,接著是兩個(gè)冒號,接著是大括號中的各自不同的路徑部分,如示例 7-18 所示。
文件名: src/main.rs
// --snip--
use std::{cmp::Ordering, io};
// --snip--
示例 7-18: 指定嵌套的路徑在一行中將多個(gè)帶有相同前綴的項(xiàng)引入作用域
在較大的程序中,使用嵌套路徑從相同包或模塊中引入很多項(xiàng),可以顯著減少所需的獨(dú)立 use
語句的數(shù)量!
我們可以在路徑的任何層級使用嵌套路徑,這在組合兩個(gè)共享子路徑的 use
語句時(shí)非常有用。例如,示例 7-19 中展示了兩個(gè) use
語句:一個(gè)將 std::io
引入作用域,另一個(gè)將 std::io::Write
引入作用域:
文件名: src/lib.rs
use std::io;
use std::io::Write;
示例 7-19: 通過兩行 ?use
?語句引入兩個(gè)路徑,其中一個(gè)是另一個(gè)的子路徑
兩個(gè)路徑的相同部分是 std::io
,這正是第一個(gè)路徑。為了在一行 use
語句中引入這兩個(gè)路徑,可以在嵌套路徑中使用 self
,如示例 7-20 所示。
文件名: src/lib.rs
use std::io::{self, Write};
示例 7-20: 將示例 7-19 中部分重復(fù)的路徑合并為一個(gè) ?use
?語句
這一行便將 std::io
和 std::io::Write
同時(shí)引入作用域。
如果希望將一個(gè)路徑下 所有 公有項(xiàng)引入作用域,可以指定路徑后跟 *
,glob 運(yùn)算符:
use std::collections::*;
這個(gè) use
語句將 std::collections
中定義的所有公有項(xiàng)引入當(dāng)前作用域。使用 glob 運(yùn)算符時(shí)請多加小心!Glob 會使得我們難以推導(dǎo)作用域中有什么名稱和它們是在何處定義的。
glob 運(yùn)算符經(jīng)常用于測試模塊 tests
中,這時(shí)會將所有內(nèi)容引入作用域;我們將在第十一章 “如何編寫測試” 部分講解。glob 運(yùn)算符有時(shí)也用于 prelude 模式;查看 標(biāo)準(zhǔn)庫中的文檔 了解這個(gè)模式的更多細(xì)節(jié)。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: