Rust从0到1-代码组织-use关键字
用于调用函数的路径都很长,而且每次调用函数的时候都要写一遍,很不方便。譬如,前面的例子中, 不管是
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();
}
例子中我们将
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();
}
use 使用惯例
在前面的例子中,我们不禁会想直接使用
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();
}
就像上面的例子,我们可以直接将
相反,使用
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert(1, 2);
}
这个习惯用法有一个例外,就是我们想使用
use std::fmt;
use std::io;
fn function1() -> fmt::Result {
// --snip--
}
fn function2() -> io::Result<()> {
// --snip--
}
当然惯例不是硬性要求,只是大部人已经习惯了以这种方式阅读和编写 Rust 代码,就像我们用一种大部人都习惯的表达方式去和别人沟通。
使用 as
将两个同名类型引入作用域还有另一个解决办法:使用
use std::fmt::Result;
use std::io::Result as IoResult;
fn function1() -> Result {
// --snip--
}
fn function2() -> IoResult<()> {
// --snip--
}
这和前面带上父模块的做法都是惯用方式,因此,我们可以根据自己的实际情况选用。
使用 pub use
当使用
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();
}
上例中我们通过
当我们根据我们所处的领域对代码进行组织,而使用我们代码的程序员所处的领域不同时,重导出将会很有用(我理解是大家所处的行业等背景不同,对代码的组织就会不同,就像DDD里所提倡的理念,保持相同的领域语言和概念进行沟通)。譬如,在前面餐馆的例子中,从经营餐馆的角度的会使用“前台”和“后台”的术语,但对于光顾一家餐馆的顾客,一般就不会使用这些术语来定义一家餐馆。通过使用 pub use,我们可以在内部使用一种代码组织结构,另外,可以对外暴露看上去不同的结构。这样可以在不改变我们内部的领域语言的同时,让开发这个库的程序员可以通过使用这个库的程序员的领域语言进行沟通(根据我的经验,沟通特别重要,也是最经常碰到的问题)。
使用外部包
我们可以通过在 Cargo.toml 中加入依赖的描述来告诉 Cargo 要从 crates.io 下载相关其依赖(避免重复造轮子;)):
[dependencies]
rand = "0.8.3"
接着,我们可以通过 use 将我们需要使用到的依赖库中的函数、结构体或枚举等引入我们代码的作用域(注意,这里的
use std::io;
use rand::Rng;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1, 101);
println!("The secret number is: {}", secret_number);
println!("Please input your guess.");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
println!("You guessed: {}", guess);
}
另外,需要注意的是标准库(std)也属于外部依赖,但是因为标准库包含在 Rust 的默认分发中,因此无需修改 Cargo.toml 来引入,不过仍然需要通过 use 将标准库中的定义引入作用域中来使用它们,比如
#![allow(unused)]
fn main() {
use std::collections::HashMap;
}
路径嵌套
当需要引入很多定义于相同包或相同模块的内容时,为每一项单独列出一行会让源码文件显得很长,也不方便阅读,譬如:
use rand::Rng;
// --snip--
use std::cmp::Ordering;
use std::io;
// --snip--
fn main() {
// --snip--
}
为了解决这个问题,Rust提供了路径嵌套的方式将相关依赖项通过一行代码引入作用域,并且可以在路径的任何层级使用路径嵌套。在比较大的源码文件中,使用路径嵌套的方式从相同包或模块中引入依赖,可以明显的减少
use std::{cmp::Ordering, io};
use std::io::{self, Write};
Glob操作符
如果我们想将一个路径下所有公有的内容引入作用域,可以其父路径后跟
#![allow(unused)]
fn main() {
use std::collections::*;
}
例子中的