外部crate
外部crate的意思是 "别人的crate"。
在本节中,你差不多需要安装Rust,但我们仍然可以只使用Playground。现在我们要学习如何导入别人写的crate。这在Rust中很重要,原因有二。
- 导入其他的crate很容易,并且...
- Rust标准库是相当小的。
这意味着,在Rust中,很多基本功能都需要用到外部Crate,这很正常。我们的想法是,如果使用外部Crate很方便,那么你可以选择最好的一个。也许一个人会为一个功能创建一个crate,然后其他人会创建一个更好的crate。
在本书中,我们只看最流行的crate,也就是每个使用Rust的人都知道的crate。
要开始学习外部Crate,我们将从最常见的Crate开始。rand
.
rand
你有没有注意到,我们还没有使用任何随机数?那是因为随机数不在标准库中。但是有很多crate "几乎是标准库",因为大家都在使用它们。在任何情况下,带入一个 crate 是非常容易的。如果你的电脑上有Rust,有一个叫Cargo.toml
的文件,里面有这些信息。Cargo.toml
文件在你启动时是这样的。
[package]
name = "rust_book"
version = "0.1.0"
authors = ["David MacLeod"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
现在,如果你想添加rand
crate,在crates.io
上搜索它,这是所有crate的去处。这将带你到https://crates.io/crates/rand
。当你点击那个,你可以看到一个屏幕,上面写着Cargo.toml rand = "0.7.3"
。你所要做的就是在[dependencies]下添加这样的内容:
[package]
name = "rust_book"
version = "0.1.0"
authors = ["David MacLeod"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rand = "0.7.3"
然后Cargo会帮你完成剩下的工作。然后你就可以在rand
文档网站上开始编写像本例代码这样的代码。要想进入文档,你可以点击crates.io上的页面中的docs
按钮。
关于Cargo的介绍就到这里了:我们现在使用的还只是playground。幸运的是,playground已经安装了前100个crate。所以你还不需要写进Cargo.toml
。在playground上,你可以想象,它有一个这样的长长的列表,有100个crate。
[dependencies]
rand = "0.7.3"
some_other_crate = "0.1.0"
another_nice_crate = "1.7"
也就是说,如果要使用rand
,你可以直接这样做:
use rand; // This means the whole crate rand // On your computer you can't just write this; // you need to write in the Cargo.toml file first fn main() { for _ in 0..5 { let random_u16 = rand::random::<u16>(); print!("{} ", random_u16); } }
每次都会打印不同的u16
号码,比如42266 52873 56528 46927 6867
。
rand
中的主要功能是random
和thread_rng
(rng的意思是 "随机数发生器")。而实际上如果你看random
,它说:"这只是thread_rng().gen()
的一个快捷方式"。所以其实是thread_rng
基本做完了一切。
下面是一个简单的例子,从1到10的数字。为了得到这些数字,我们在1到11之间使用.gen_range()
。
use rand::{thread_rng, Rng}; // Or just use rand::*; if we are lazy fn main() { let mut number_maker = thread_rng(); for _ in 0..5 { print!("{} ", number_maker.gen_range(1, 11)); } }
这将打印出7 2 4 8 6
这样的东西。
用随机数我们可以做一些有趣的事情,比如为游戏创建角色。我们将使用rand
和其他一些我们知道的东西来创建它们。在这个游戏中,我们的角色有六种状态,用一个d6来表示他们。d6是一个立方体,当你投掷它时,它能给出1、2、3、4、5或6。每个角色都会掷三次d6,所以每个统计都在3到18之间。
但是有时候如果你的角色有一些低的东西,比如3或4,那就不公平了。比如说你的力量是3,你就不能拿东西。所以还有一种方法是用d6四次。你掷四次,然后扔掉最低的数字。所以如果你掷3,3,1,6,那么你保留3,3,6=12。我们也会把这个方法做出来,所以游戏的主人可以决定。
这是我们简单的角色创建器。我们为数据统计创建了一个Character
结构,甚至还实现了Display
来按照我们想要的方式打印。
use rand::{thread_rng, Rng}; // Or just use rand::*; if we are lazy use std::fmt; // Going to impl Display for our character struct Character { strength: u8, dexterity: u8, // This means "body quickness" constitution: u8, // This means "health" intelligence: u8, wisdom: u8, charisma: u8, // This means "popularity with people" } fn three_die_six() -> u8 { // A "die" is the thing you throw to get the number let mut generator = thread_rng(); // Create our random number generator let mut stat = 0; // This is the total for _ in 0..3 { stat += generator.gen_range(1..=6); // Add each time } stat // Return the total } fn four_die_six() -> u8 { let mut generator = thread_rng(); let mut results = vec![]; // First put the numbers in a vec for _ in 0..4 { results.push(generator.gen_range(1..=6)); } results.sort(); // Now a result like [4, 3, 2, 6] becomes [2, 3, 4, 6] results.remove(0); // Now it would be [3, 4, 6] results.iter().sum() // Return this result } enum Dice { Three, Four } impl Character { fn new(dice: Dice) -> Self { // true for three dice, false for four match dice { Dice::Three => Self { strength: three_die_six(), dexterity: three_die_six(), constitution: three_die_six(), intelligence: three_die_six(), wisdom: three_die_six(), charisma: three_die_six(), }, Dice::Four => Self { strength: four_die_six(), dexterity: four_die_six(), constitution: four_die_six(), intelligence: four_die_six(), wisdom: four_die_six(), charisma: four_die_six(), }, } } fn display(&self) { // We can do this because we implemented Display below println!("{}", self); println!(); } } impl fmt::Display for Character { // Just follow the code for in https://doc.rust-lang.org/std/fmt/trait.Display.html and change it a bit fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "Your character has these stats: strength: {} dexterity: {} constitution: {} intelligence: {} wisdom: {} charisma: {}", self.strength, self.dexterity, self.constitution, self.intelligence, self.wisdom, self.charisma ) } } fn main() { let weak_billy = Character::new(Dice::Three); let strong_billy = Character::new(Dice::Four); weak_billy.display(); strong_billy.display(); }
它会打印出这样的东西。
#![allow(unused)] fn main() { Your character has these stats: strength: 9 dexterity: 15 constitution: 15 intelligence: 8 wisdom: 11 charisma: 9 Your character has these stats: strength: 9 dexterity: 13 constitution: 14 intelligence: 16 wisdom: 16 charisma: 10 }
有四个骰子的角色通常在大多数事情上都会好一点。
rayon
rayon
是一个流行的crate,它可以让你加快 Rust 代码的速度。它之所以受欢迎,是因为它无需像 thread::spawn
这样的东西就能创建线程。换句话说,它之所以受欢迎是因为它既有效又容易编写。比如说
.iter()
,.iter_mut()
,into_iter()
在rayon中是这样写的:.par_iter()
,.par_iter_mut()
,par_into_iter()
. 所以你只要加上par_
,你的代码就会变得快很多。(par的意思是 "并行")
其他方法也一样:.chars()
就是.par_chars()
,以此类推。
这里举个例子,一段简单的代码,却让计算机做了很多工作。
fn main() { let mut my_vec = vec![0; 200_000]; my_vec.iter_mut().enumerate().for_each(|(index, number)| *number+=index+1); println!("{:?}", &my_vec[5000..5005]); }
它创建了一个有20万项的向量:每一项都是0,然后调用.enumerate()
来获取每个数字的索引,并将0改为索引号。它的打印时间太长,所以我们只打印5000到5004项。这在Rust中还是非常快的,但如果你愿意,你可以用Rayon让它更快。代码几乎是一样的。
use rayon::prelude::*; // Import rayon fn main() { let mut my_vec = vec![0; 200_000]; my_vec.par_iter_mut().enumerate().for_each(|(index, number)| *number+=index+1); // add par_ to iter_mut println!("{:?}", &my_vec[5000..5005]); }
就这样了。rayon
还有很多其他的方法来定制你想做的事情,但最简单的就是 "添加_par
,让你的程序更快"。
serde
serde
是一个流行的crate,它可以在JSON、YAML等格式间相互转换。最常见的使用方法是通过创建一个struct
,上面有两个属性。它看起来是这样的。
#![allow(unused)] fn main() { #[derive(Serialize, Deserialize, Debug)] struct Point { x: i32, y: i32, } }
Serialize
和Deserialize
trait是使转换变得简单的原因。(这也是serde
这个名字的由来)如果你的结构体上有这两个trait,那么你只需要调用一个方法就可以把它转化为JSON或其他任何东西。
regex
regex crate 可以让你使用 正则表达式 搜索文本。有了它,你可以通过一次搜索得到诸如 colour
, color
, colours
和 colors
的匹配信息。正则表达式是另一门语言,如果你想使用它们,也必须学会。
chrono
chrono是为那些需要更多时间功能的人准备的主要crate。我们现在来看一下标准库,它有时间的功能,但是如果你需要更多的功能,那么这个crate是一个不错的选择。