
Rust 学习笔记
自从在工作是使用了Go语言后,就不断的打破原来的模式,想什么都用Go来实现,可惜有些东西,特别是Windows下的窗口程序,就无能为力了,其他的好像都可以试一试。因为关注Go,所以Node的作者想重新写Node的时候,选择了Go,最后居然换成了Rust。这不得不让我对这门语言有了新的看法,特别是微软和Google都要推Rust的时候,我放弃了我原来对这门语言的偏见,重新、认真的学习一遍,让我看看到底是什么让它受到那么多人的关注。
前言
系统级别的语言,可以和C/C++抗衡?目标就是为了取代C++。
介绍
Rust语言能够编写速度更快,更加可靠的软件。
学习笔记为1.52.1版本,如果你来晚了,最好参考 最新文档 来进行学习。你懂的~~
0x01 开始
1.目标
- 安装
- 写个输出hello world的程序
- 使用cargo工具来构建系统
2 安装
2.1 安装
其实在苹果电脑上,能用brew安装的,我一般都会使用。可惜,Rust推荐的最佳方式,并不是。😊
所以,使用Rust的安装方式,最佳。
$ curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh
安装成功后,将在命令端输出:
Rust is installed now. Great!
2.2 更新
$ rustup update
2.3 删除
$ rustup self uninstall
使用了许多语言后,这个是发现最贴心的。
2.4 版本查看
$ rustc --version
2.5 阅读本地文档
rustup doc
3. Hello,World!
3.1 创建项目路径
$ mkdir ~/projects
$ cd ~/projects
$ mkdir hello_world
$ cd hello_world
3.2 编写和运行Rust程序
创建main.rs文件,编写以下代码:
fn main() {
println!("Hello, world!");
}
编译和运行代码:
$ rustc main.rs
$ ./main
Hello, world!
rustc 编译单一的程序比较好,但是到真正的项目中还是推荐使用Cargo工具进行编译。
4. Hello,Cargo!
Cargo是Rust编译系统和包管理工具。
验证cargo是否安装成功:
cargo --version
4.1 使用Cargo创建项目
$ cargo new hello_cargo
$ cd hello_cargo
4.2 编译和运行Cargo项目
编译代码:
$ cargo build
路径下执行代码:
$ ./target/debug/hello_cargo
执行代码。
$ cargo run
检查代码,但是不产生可执行文件。
$ cargo check
4.3 编译发布版本
$ cargo build --release
5. 猜谜程序
5.1 创建一个新项目
$ cargo new guessing_game
$ cd guessing_game
5.2 处理猜测
use std::io;
fn main() {
println!("Guess the 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);
}
5.3 数值存储
let mut guess = String::new();
5.4 处理异常
.expect("Failed to read line");
5.5 使用println!函数打印
println!("You guessed: {}", guess);
5.6 添加依赖项
在文件Cargo.toml中添加:
[dependencies]
rand = "0.8.3"
5.7 更新项目
$ cargo build
5.8 更新依赖项版本
$ cargo update
Updating crates.io index
Updating rand v0.8.3 -> v0.8.4
此时rand版本更新支最新。
或者直接打开 Cargo.toml,修改版本。
[dependencies]
rand = "0.9.0"
5.9 创建一个随机数
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);
}
5.10 比较大小
use rand::Rng;
use std::cmp::Ordering;
use std::io;
fn main() {
// --snip--
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => println!("You win!"),
}
}
5.11 添加循环
loop {
println!("Please input your guess.");
// --snip--
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => println!("You win!"),
}
}
5.12 跳出循环
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
break;
}
}
5.13 处理异常输入
// --snip--
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
println!("You guessed: {}", guess);
// --snip--
6. 常见编程概念
6.1 变量和可变性
let x = 6; //x不能被赋值
let mut x =6; //x能被赋值
6.2 变量与常量的差异
const MAX_POINTS: u32 = 100_000;
6.3 阴影效果
let spaces = " ";
let spaces = spaces.len();
let 再次使用关键字时,重新创建了一个新变量。
但是以下的定义为错误:
let mut spaces = " ";
spaces = spaces.len();
此时spaces将变改变为整型,而非原来的字符型。
6.4 数据类型
Rust是静态类型语言,编译时需要知道变量的类型。
例如:
let guess: u32 = "42".parse().expect("Not a number!");
如果没有类型注释,如下所示:
let guess = "42".parse().expect("Not a number!");
编译将出现错误。
$ cargo build
Compiling no_type_annotations v0.1.0 (file:///projects/no_type_annotations)
error[E0282]: type annotations needed
--> src/main.rs:2:9
|
2 | let guess = "42".parse().expect("Not a number!");
| ^^^^^ consider giving `guess` a type
error: aborting due to previous error
For more information about this error, try `rustc --explain E0282`.
error: could not compile `no_type_annotations`
To learn more, run the command again with --verbose.
6.4.1 标量类型
Rust 有四种主要的标量类型:整数、浮点数、布尔值和字符。
- 1.整数类型
Length | Signed | Unsigned |
---|---|---|
8-bit | i8 | u8 |
16-bit | i16 | u16 |
32-bit | i32 | u32 |
128-bit | i128 | u128 |
arch | isize | usize |
Number literals | Example |
---|---|
Decimal | 98_222 |
Hex | 0xff |
Octal | 0o77 |
Binary | 0b1111_0000 |
Byte | (u8 only) b’A' |
- 2.浮点类型
Rust 也有两种用于浮点数的原始类型,它们是带小数点的数字。Rust 的浮点类型是f32and f64,它们的大小分别是 32 位和 64 位。默认类型是f64。 因为在现代 CPU 上它的速度大致相同,f32但精度更高。
这是一个显示浮点数的示例:
fn main() {
let x = 2.0; // f64
let y: f32 = 3.0; // f32
}
浮点数根据 IEEE-754 标准表示。该 f32类型是单精度浮点数,f64具有双精度。
- 3.数值运算
fn main() {
// addition
let sum = 5 + 10;
// subtraction
let difference = 95.5 - 4.3;
// multiplication
let product = 4 * 30;
// division
let quotient = 56.7 / 32.2;
// remainder
let remainder = 43 % 5;
}
- 4.布尔类型
与大多数其他编程语言一样,Rust 中的布尔类型有两个可能的值:true和false。布尔值大小为 1 个字节。Rust 中的布尔类型使用bool. 例如:
fn main() {
let t = true;
let f: bool = false; // with explicit type annotation
}
- 5.字符类型
Rust 的char类型大小为四个字节,表示一个 Unicode 标量值,这意味着它可以表示的不仅仅是 ASCII。
fn main() {
let c = 'z';
let z = 'ℤ';
let heart_eyed_cat = '😻';
}
- 6.复合类型
复合类型可以将多个值分组为一种类型。Rust 有两种原始的复合类型:元组和数组。
1.元组类型
元组是将具有多种类型的多个值组合成一个复合类型的通用方法。元组有固定的长度:一旦声明,它们的大小就不能增长或缩小。
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
}
也可以使用模式匹配来解构元组值,如下所示:
fn main() {
let tup = (500, 6.4, 1);
let (x, y, z) = tup;
println!("The value of y is: {}", y);
}
除了通过模式匹配进行解构之外,我们还可以通过使用句点 ( .) 后跟我们想要访问的值的索引来直接访问元组元素。例如:
fn main() {
let x: (i32, f64, u8) = (500, 6.4, 1);
let five_hundred = x.0;
let six_point_four = x.1;
let one = x.2;
}
该程序创建一个元组 ,x然后使用它们各自的索引为每个元素创建新变量。与大多数编程语言一样,元组中的第一个索引是 0。
- 7.数组类型
在 Rust 中,进入数组的值被写为方括号内的逗号分隔列表:
fn main() {
let a = [1, 2, 3, 4, 5];
}
在需要知道一年中月份名称的程序中,您可能想要使用数组而不是向量的示例。这样的程序不太可能需要添加或删除月份,因此您可以使用数组,因为您知道它始终包含 12 个元素:
let months = ["January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December"];
您可以使用方括号编写数组的类型,方括号内包含每个元素的类型、分号,然后是数组中的元素数,如下所示:
let a: [i32; 5] = [1, 2, 3, 4, 5];
这里,i32是每个元素的类型。分号后的数字5 表示数组包含五个元素。
以这种方式编写数组的类型看起来类似于初始化数组的另一种语法:如果您想创建一个数组,其中每个元素都包含相同的值,您可以指定初始值,后跟一个分号,然后是长度方括号中的数组,如下所示:
let a = [3; 5];
命名的数组a将包含5所有3最初设置为该值的元素 。这与写作相同,let a = [3, 3, 3, 3, 3];但方式更简洁。
访问数组元素,可以使用索引访问数组的元素,如下所示:
fn main() {
let a = [1, 2, 3, 4, 5];
let first = a[0];
let second = a[1];
}
在这个例子中,named 的变量first将获得 value 1,因为这是[0]数组中索引处的值。命名的变量second将从数组中的2索引[1]中获取值。
6.5 函数
函数在 Rust 代码中无处不在。您已经看到了该语言中最重要的函数之一:main函数,它是许多程序的入口点。您还看到了fn关键字,它允许您声明新函数。
Rust 代码使用蛇形大小写作为函数和变量名称的常规样式。在蛇的情况下,所有字母都是小写的,并用下划线分隔单词。这是一个包含示例函数定义的程序:
fn main() {
println!("Hello, world!");
another_function();
}
fn another_function() {
println!("Another function.");
}
Rust 中的函数定义以fn函数名开头,并在函数名后有一组括号。大括号告诉编译器函数体的开始和结束位置。
Rust 不关心你在哪里定义你的函数,只关心它们是在某处定义的。
- 1.功能参数
fn main() {
another_function(5);
}
fn another_function(x: i32) {
println!("The value of x is: {}", x);
}
一个函数有多个参数时,用逗号分隔参数声明,像这样:
fn main() {
another_function(5, 6);
}
fn another_function(x: i32, y: i32) {
println!("The value of x is: {}", x);
println!("The value of y is: {}", y);
}
创建新作用域的块{}是一个表达式,例如:
n main() {
let x = 5;
let y = {
let x = 3;
x + 1
};
println!("The value of y is: {}", y);
}
- 2.具有返回值的函数
函数可以将值返回给调用它们的代码。我们不命名返回值,但我们确实在箭头 ( ->)后声明它们的类型。在 Rust 中,函数的返回值与函数体块中最终表达式的值是同义词。您可以通过使用return关键字并指定值从函数中提前返回,但大多数函数会隐式返回最后一个表达式。下面是一个返回值的函数示例:
fn five() -> i32 {
5
}
fn main() {
let x = five();
println!("The value of x is: {}", x);
}
6.6 注释
在 Rust 中,惯用的注释样式以两个斜杠开始注释,并且注释一直持续到行尾。对于超出单行的注释,您需要//在每一行中包含,如下所示:
// So we’re doing something complicated here, long enough that we need
// multiple lines of comments to do it! Whew! Hopefully, this comment will
// explain what’s going on.
注释也可以放在包含代码的行的末尾:
fn main() {
let lucky_number = 7; // I’m feeling lucky today
}
但是你会更经常地看到它们以这种格式使用,注释在它所注释的代码上方的单独行上:
fn main() {
// I’m feeling lucky today
let lucky_number = 7;
}
Rust 还有另一种注释,文档注释,我们将在第 14 章的“将 Crate 发布到 Crates.io”一节中讨论。
6.7控制流
- 1.if表达式
fn main() {
let number = 3;
if number < 5 {
println!("condition was true");
} else {
println!("condition was false");
}
}
必须明确并始终提供 if布尔值作为其条件。
fn main() {
let number = 3;
if number != 0 {
println!("number was something other than zero");
}
}
- 2.处理多个条件 else if
fn main() {
let number = 6;
if number % 4 == 0 {
println!("number is divisible by 4");
} else if number % 3 == 0 {
println!("number is divisible by 3");
} else if number % 2 == 0 {
println!("number is divisible by 2");
} else {
println!("number is not divisible by 4, 3, or 2");
}
}
- 3.使用if的let语句
fn main() {
let condition = true;
let number = if condition { 5 } else { 6 };
println!("The value of number is: {}", number);
}
- 4.循环重复
Rust有三种循环:loop,while,和for。
(1)重复代码 loop
该loop关键字告诉Rust永远比执行的代码块一遍又一遍,或者直到你明确告诉它停止。
fn main() {
loop {
println!("again!");
}
}
从循环返回值
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
println!("The result is {}", result);
}
(2)条件循环 while
fn main() {
let mut number = 3;
while number != 0 {
println!("{}!", number);
number -= 1;
}
println!("LIFTOFF!!!");
}
(3)循环遍历集合 for
while方式:
fn main() {
let a = [10, 20, 30, 40, 50];
let mut index = 0;
while index < 5 {
println!("the value is: {}", a[index]);
index += 1;
}
}
for 方式:
fn main() {
let a = [10, 20, 30, 40, 50];
for element in a.iter() {
println!("the value is: {}", element);
}
}
7. 所有权
所有权是 Rust 最独特的特性,它使 Rust 能够在不需要垃圾收集器的情况下做出内存安全保证。因此,了解所有权在 Rust 中的运作方式非常重要。在本章中,我们将讨论所有权以及几个相关的特性:借用、切片,以及 Rust 如何在内存中布置数据。
1.所有权规则
- Rust 中的每个值都有一个变量,称为其owner。
- 一次只能有一个所有者。
- 当所有者超出范围时,该值将被删除。
读不懂了???
8.结构
8.1 定义和实例化结构
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
let user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
8.2 结构方法语法
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!(
"The area of the rectangle is {} square pixels.",
rect1.area()
);
}
9.枚举和模式匹配
9.1 定义枚举
enum IpAddrKind {
V4,
V6,
}
9.2 枚举值
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
结合结构和枚举的使用:
enum IpAddrKind {
V4,
V6,
}
struct IpAddr {
kind: IpAddrKind,
address: String,
}
let home = IpAddr {
kind: IpAddrKind::V4,
address: String::from("127.0.0.1"),
};
let loopback = IpAddr {
kind: IpAddrKind::V6,
address: String::from("::1"),
};
9.3 匹配控制流程操作符
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
9.4 “_“占位符
该_模式将匹配任何值。
let some_u8_value = 0u8;
match some_u8_value {
1 => println!("one"),
3 => println!("three"),
5 => println!("five"),
7 => println!("seven"),
_ => (),
}
9.5 if let
let some_u8_value = Some(0u8);
match some_u8_value {
Some(3) => println!("three"),
_ => (),
}
10.管理不断增长的项目
-
Packages: A Cargo feature that lets you build, test, and share crates
-
Crates: A tree of modules that produces a library or executable
-
Modules and use: Let you control the organization, scope, and privacy of paths
-
Paths: A way of naming an item, such as a struct, function, or module
10.1 Packages and Crates
cargo new my-project
Created binary (application) `my-project` package
$ ls my-project
Cargo.toml
src
$ ls my-project/src
main.rs
10.2 定义模块以控制范围和隐私
文件名:src/lib.rs
mod front_of_house {
mod hosting {
fn add_to_waitlist() {}
fn seat_at_table() {}
}
mod serving {
fn take_order() {}
fn serve_order() {}
fn take_payment() {}
}
}
模块树:
crate
└── front_of_house
├── hosting
│ ├── add_to_waitlist
│ └── seat_at_table
└── serving
├── take_order
├── serve_order
└── take_payment
10.3 引用模块树中项目的路径
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
pub fn eat_at_restaurant() {
// Absolute path
crate::front_of_house::hosting::add_to_waitlist();
// Relative path
front_of_house::hosting::add_to_waitlist();
}
10.4 使用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();
}
10.5 将模块分成不同的文件
mod front_of_house;
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
hosting::add_to_waitlist();
hosting::add_to_waitlist();
}
11 常见集合
Rust 的标准库包括许多非常有用的数据结构,称为集合。
11.1 用向量存储值列表
我们将看到的第一个集合类型是Vec,也称为向量。向量允许您在单个数据结构中存储多个值,该数据结构将所有值并排放置在内存中。向量只能存储相同类型的值。当您有一个项目列表时,它们很有用,例如文件中的文本行或购物车中项目的价格。
- 1.创建新向量
let v: Vec<i32> = Vec::new();
- 2.更新向量
let mut v = Vec::new();
v.push(5);
v.push(6);
v.push(7);
v.push(8);
11.2 存储UTF-8字符串
let hello = String::from("السلام عليكم");
let hello = String::from("Dobrý den");
let hello = String::from("Hello");
let hello = String::from("שָׁלוֹם");
let hello = String::from("नमस्ते");
let hello = String::from("こんにちは");
let hello = String::from("안녕하세요");
let hello = String::from("你好");
let hello = String::from("Olá");
let hello = String::from("Здравствуйте");
let hello = String::from("Hola");
- 1.添加字符串
let mut s = String::from("lo");
s.push('l');
使用操作符 “+“进行字符串操作:
let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2; // note s1 has been moved here and can no longer be used
11.3 HashMap
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
//Accessing Values in a Hash Map
let team_name = String::from("Blue");
let score = scores.get(&team_name);
for (key, value) in &scores {
println!("{}: {}", key, value);
}
//Overwriting a Value
scores.insert(String::from("Blue"), 25);
12 错误处理
12.1 panic
fn main() {
panic!("crash and burn");
}
RUST_BACKTRACE=1 cargo run
打印错误详细信息。
12.2 带错误信息的错误处理
use std::fs::File;
fn main() {
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => panic!("Problem opening the file: {:?}", error),
};
}
多错误处理:
use std::fs::File;
use std::io::ErrorKind;
fn main() {
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => match error.kind() {
ErrorKind::NotFound => match File::create("hello.txt") {
Ok(fc) => fc,
Err(e) => panic!("Problem creating the file: {:?}", e),
},
other_error => {
panic!("Problem opening the file: {:?}", other_error)
}
},
};
}
12.3 何时使用panic
pub struct Guess {
value: i32,
}
impl Guess {
pub fn new(value: i32) -> Guess {
if value < 1 || value > 100 {
panic!("Guess value must be between 1 and 100, got {}.", value);
}
Guess { value }
}
pub fn value(&self) -> i32 {
self.value
}
}
13.泛型类型、特性和生命周期
提取函数,去重复
fn main() {
let number_list = vec![34, 50, 25, 100, 65];
let mut largest = number_list[0];
for number in number_list {
if number > largest {
largest = number;
}
}
println!("The largest number is {}", largest);
let number_list = vec![102, 34, 6000, 89, 54, 2, 43, 8];
let mut largest = number_list[0];
for number in number_list {
if number > largest {
largest = number;
}
}
println!("The largest number is {}", largest);
}
提取后,将转变为以下函数:
fn largest(list: &[i32]) -> i32 {
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let number_list = vec![34, 50, 25, 100, 65];
let result = largest(&number_list);
println!("The largest number is {}", result);
let number_list = vec![102, 34, 6000, 89, 54, 2, 43, 8];
let result = largest(&number_list);
println!("The largest number is {}", result);
}
13.1 通用数据类型
- 1.函数定义
fn largest_i32(list: &[i32]) -> i32 {
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}
fn largest_char(list: &[char]) -> char {
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let number_list = vec![34, 50, 25, 100, 65];
let result = largest_i32(&number_list);
println!("The largest number is {}", result);
let char_list = vec!['y', 'm', 'a', 'q'];
let result = largest_char(&char_list);
println!("The largest char is {}", result);
}
将数字比较和字符比较,都使用泛型的方式:
fn largest<T>(list: &[T]) -> T {
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let number_list = vec![34, 50, 25, 100, 65];
let result = largest(&number_list);
println!("The largest number is {}", result);
let char_list = vec!['y', 'm', 'a', 'q'];
let result = largest(&char_list);
println!("The largest char is {}", result);
}
- 2.结构定义
struct Point<T, U> {
x: T,
y: U,
}
fn main() {
let both_integer = Point { x: 5, y: 10 };
let both_float = Point { x: 1.0, y: 4.0 };
let integer_and_float = Point { x: 5, y: 4.0 };
}
- 2.枚举定义
enum Option<T> {
Some(T),
None,
}
或者
enum Result<T, E> {
Ok(T),
Err(E),
}
- 3.方法定义
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
fn main() {
let p = Point { x: 5, y: 10 };
println!("p.x = {}", p.x());
}
一个更加复杂的例子:
struct Point<T, U> {
x: T,
y: U,
}
impl<T, U> Point<T, U> {
fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
Point {
x: self.x,
y: other.y,
}
}
}
fn main() {
let p1 = Point { x: 5, y: 10.4 };
let p2 = Point { x: "Hello", y: 'c' };
let p3 = p1.mixup(p2);
println!("p3.x = {}, p3.y = {}", p3.x, p3.y);
}
-
- 代码定义
let integer = Some(5);
let float = Some(5.0);
13.2 特性:定义共享行为
定义特性:
pub trait Summary {
fn summarize(&self) -> String;
}
在结构中分别实现:
pub struct NewsArticle {
pub headline: String,
pub location: String,
pub author: String,
pub content: String,
}
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("{}, by {} ({})", self.headline, self.author, self.location)
}
}
pub struct Tweet {
pub username: String,
pub content: String,
pub reply: bool,
pub retweet: bool,
}
impl Summary for Tweet {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}
函数中使用特性:
fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let number_list = vec![34, 50, 25, 100, 65];
let result = largest(&number_list);
println!("The largest number is {}", result);
let char_list = vec!['y', 'm', 'a', 'q'];
let result = largest(&char_list);
println!("The largest char is {}", result);
}
13.3 引用生命周期
- 1.函数中的生命周期
fn main() {
let string1 = String::from("abcd");
let string2 = "xyz";
let result = longest(string1.as_str(), string2);
println!("The longest string is {}", result);
}
- 2.生命周期注释语法
&i32 // a reference
&'a i32 // a reference with an explicit lifetime
&'a mut i32 // a mutable reference with an explicit lifetime
- 3.函数中的生命周期注释
fn longest<'a>(x: &'a str, y: &str) -> &'a str {
x
}
- 4.静态生命周期
let s: &'static str = "I have a static lifetime.";
- 5.通用类型参数、特征边界和生命周期
use std::fmt::Display;
fn longest_with_an_announcement<'a, T>(
x: &'a str,
y: &'a str,
ann: T,
) -> &'a str
where
T: Display,
{
println!("Announcement! {}", ann);
if x.len() > y.len() {
x
} else {
y
}
}
14. 编写自动化测试
cargo test --help
14.1 如何编写测试
- 1.编写自动化测试
#[cfg(test)]
mod tests {
#[test]
fn exploration() {
assert_eq!(2 + 2, 4);
}
#[test]
fn another() {
panic!("Make this test fail");
}
}
14.2
并行测试
$ cargo test -- --test-threads=1
显示输出信息:
cargo test -- --show-output
测试函数:
pub fn add_two(a: i32) -> i32 {
a + 2
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn add_two_and_two() {
assert_eq!(4, add_two(2));
}
#[test]
fn add_three_and_two() {
assert_eq!(5, add_two(3));
}
#[test]
fn one_hundred() {
assert_eq!(102, add_two(100));
}
}
cargo test one_hundred
匹配函数名称进行测试,所有的add开头都会被测试:
cargo test add
忽略测试标记:
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
#[test]
#[ignore]
fn expensive_test() {
// code that takes an hour to run
}
cargo test -- --ignored
14.3 测试结构
- 1.单元测试
#[cfg(test)]
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}
- 2.私有函数测试
pub fn add_two(a: i32) -> i32 {
internal_adder(a, 2)
}
fn internal_adder(a: i32, b: i32) -> i32 {
a + b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn internal() {
assert_eq!(4, internal_adder(2, 2));
}
}
15. 一个读写项目:构建一个命令行程序
15.1 接收命令行参数
- 1.读取参数
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
println!("{:?}", args);
}
- 2.存储参数
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
let query = &args[1];
let filename = &args[2];
println!("Searching for {}", query);
println!("In file {}", filename);
}
15.2 读取文件
use std::env;
use std::fs;
fn main() {
let args: Vec<String> = env::args().collect();
let query = &args[1];
let filename = &args[2];
println!("Searching for {}", query);
println!("In file {}", filename);
let contents = fs::read_to_string(filename)
.expect("Something went wrong reading the file");
println!("With text:\n{}", contents);
}
15.3 提高模块化和错误处理
lib.rs
use std::error::Error;
use std::fs;
pub struct Config {
pub query: String,
pub filename: String,
}
impl Config {
pub fn new(args: &[String]) -> Result<Config, &str> {
if args.len() < 3 {
panic!("not enough arguments");
}
let query = args[1].clone();
let filename = args[2].clone();
Ok(Config { query, filename })
}
}
pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
let contents = fs::read_to_string(config.filename)?;
println!("With text:\n{}", contents);
Ok(())
}
main.rs
use std::env;
use std::process;
use minigrep::Config;
fn main() {
let args: Vec<String> = env::args().collect();
let config = Config::new(&args).unwrap_or_else(|err| {
println!("Problem parsing arguments: {}", err);
process::exit(1);
});
println!("Searching for {}", config.query);
println!("In file {}", config.filename);
if let Err(e) = minigrep::run(config) {
println!("Application error: {}", e);
process::exit(1);
}
}
15.4 使用测试驱动开发开发库的功能
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn one_result() {
let query = "duct";
let contents = "\
Rust:
safe, fast, productive.
Pick three.";
assert_eq!(vec!["safe, fast, productive."], search(query, contents));
}
}
15.5 使用环境变量
CASE_INSENSITIVE=1 cargo run to poem.txt
15.6 将错误写出到标准输出
cargo run > output.txt
使用eprintln!() 代替 println!()。
16. 函数式语言特性:迭代器和闭包
16.1 创建闭包抽象行为
use std::thread;
use std::time::Duration;
fn simulated_expensive_calculation(intensity: u32) -> u32 {
println!("calculating slowly...");
thread::sleep(Duration::from_secs(2));
intensity
}
16.2 使用迭代器处理一系列项目
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter();
for val in v1_iter {
println!("Got: {}", val);
}
16.3 改进I/O项目
impl Config {
pub fn new(mut args: env::Args) -> Result<Config, &'static str> {
args.next();
let query = match args.next() {
Some(arg) => arg,
None => return Err("Didn't get a query string"),
};
let filename = match args.next() {
Some(arg) => arg,
None => return Err("Didn't get a file name"),
};
let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
Ok(Config {
query,
filename,
case_sensitive,
})
}
}
pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
contents
.lines()
.filter(|line| line.contains(query))
.collect()
}
16.4 比较性能:循环与迭代器
闭包和迭代器是受函数式编程语言思想启发的 Rust 特性。它们有助于 Rust 以低级性能清晰表达高级思想的能力。闭包和迭代器的实现不会影响运行时性能。这是 Rust 努力提供零成本抽象目标的一部分。
17. 更多的关于Cargo和Crates.io
- 通过发布配置文件自定义您的构建
- 在crates.io上发布库
- 使用工作区组织大型项目
- 从crates.io安装二进制文件
- 使用自定义命令扩展 Cargo
17.1 使用发布配置文件自定义构建
Cargo 有两个主要配置文件:devCargo 在您运行时使用cargo build的release配置文件和Cargo 在您运行时使用的配置文件cargo build –release。该dev配置文件为开发定义了良好的默认值,并且该release配置文件具有用于发布构建的良好默认值。
$ cargo build
Finished dev [unoptimized + debuginfo] target(s) in 0.0s
$ cargo build --release
Finished release [optimized] target(s) in 0.0s
此构建输出中显示的dev和release表明编译器正在使用不同的配置文件。
文件名:Cargo.toml
[profile.dev]
opt-level = 0
[profile.release]
opt-level = 3
该opt-level设置控制 Rust 将应用于您的代码的优化数量,范围为 0 到 3。应用更多优化会延长编译时间,因此如果您正在开发并经常编译您的代码,您将需要更快的编译,即使生成的代码运行速度较慢。这就是原因,默认opt-level的dev就是0。当您准备好发布代码时,最好花更多时间编译。您只会在发布模式下编译一次,但您将多次运行已编译的程序,因此发布模式会用更长的编译时间来换取运行速度更快的代码。这就是为什么默认opt-level的release配置文件3。
文件名:Cargo.toml
[profile.dev]
opt-level = 1
此代码覆盖 的默认设置0。现在,当我们运行时cargo build,Cargo 将使用dev配置文件的默认值加上我们对 opt-level. 因为我们设置opt-level为1,Cargo 将应用比默认更多的优化,但不会像在发布版本中那么多。
17.2 将 Crate 发布到 Crates.io
-
- 进行有用的文档评论
文档注释使用三个斜杠 ,///而不是两个,并且支持 Markdown 表示法来格式化文本。将文档注释放在他们正在记录的项目之前。
函数文档注释例子:
/// Adds one to the number given.
///
/// # Examples
///
/// ```
/// let arg = 5;
/// let answer = my_crate::add_one(arg);
///
/// assert_eq!(6, answer);
/// ```
pub fn add_one(x: i32) -> i32 {
x + 1
}
-
- 设置 Crates.io 帐户
在发布任何 crate 之前,您需要在crates.io上创建一个帐户 并获取 API 令牌。为此,请访问crates.io 的主页并通过 GitHub 帐户登录。(GitHub 帐户目前是必需的,但该站点将来可能会支持其他创建帐户的方式。)登录后,请访问https://crates.io/me/ 上的帐户设置并检索您的帐户 API 密钥。然后cargo login使用您的 API 密钥运行命令,如下所示:
$ cargo login abcdefghijklmnopqrstuvwxyz012345
- 3 将元数据添加到新的 Crate
文件名:Cargo.toml
[package]
name = "guessing_game"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
description = "A fun game where you guess what number the computer has chosen."
license = "MIT OR Apache-2.0"
$ cargo publish
-
- 发布到 Crates.io
cargo publish再次运行命令。现在应该成功了:
$ cargo publish
Updating crates.io index
Packaging guessing_game v0.1.0 (file:///projects/guessing_game)
Verifying guessing_game v0.1.0 (file:///projects/guessing_game)
Compiling guessing_game v0.1.0
(file:///projects/guessing_game/target/package/guessing_game-0.1.0)
Finished dev [unoptimized + debuginfo] target(s) in 0.19s
Uploading guessing_game v0.1.0 (file:///projects/guessing_game)
-
- 从 Crates.io 中删除版本 cargo yank
$ cargo yank --vers 1.0.1
$ cargo yank --vers 1.0.1 --undo
17.3 Cargo工作区域
-
- 创建工作区域
$ mkdir add
$ cd add
[workspace]
members = [
"adder",
]
$ cargo new adder
Created binary (application) `adder` package
执行cargo builh后的文件目录如下:
├── Cargo.lock
├── Cargo.toml
├── adder
│ ├── Cargo.toml
│ └── src
│ └── main.rs
└── target
-
- 在工作区域中创建第二个包
[workspace]
members = [
"adder",
"add-one",
]
$ cargo new add-one --lib
Created library `add-one` package
├── Cargo.lock
├── Cargo.toml
├── add-one
│ ├── Cargo.toml
│ └── src
│ └── lib.rs
├── adder
│ ├── Cargo.toml
│ └── src
│ └── main.rs
└── target
-
- 在工作区域中添加依赖包
[dependencies]
rand = "0.8.3"
-
- 在工作区域中添加测试
pub fn add_one(x: i32) -> i32 {
x + 1
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
assert_eq!(3, add_one(2));
}
}
17.4 从 Crates.io 安装二进制文件 cargo install
该cargo install命令允许您在本地安装和使用二进制包。安装的所有二进制文件cargo install都存储在安装根目录的bin文件夹中。
$ cargo install ripgrep
Updating crates.io index
Downloaded ripgrep v11.0.2
Downloaded 1 crate (243.3 KB) in 0.88s
Installing ripgrep v11.0.2
--snip--
Compiling ripgrep v11.0.2
Finished release [optimized + debuginfo] target(s) in 3m 10s
Installing ~/.cargo/bin/rg
Installed package `ripgrep v11.0.2` (executable `rg`)
17.5 使用自定义命令扩展 Cargo
cargo --list
18. 智能指针
18.1 使用B指向堆栈数据
fn main() {
let b = Box::new(5);
println!("b = {}", b);
}
18.2 将智能指针视为具有Dereftrait 的常规引用
fn main() {
let m = MyBox::new(String::from("Rust"));
hello(&m);
}
18.3 使用Drop特征在清理时运行代码
struct CustomSmartPointer {
data: String,
}
impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println!("Dropping CustomSmartPointer with data `{}`!", self.data);
}
}
fn main() {
let c = CustomSmartPointer {
data: String::from("my stuff"),
};
let d = CustomSmartPointer {
data: String::from("other stuff"),
};
println!("CustomSmartPointers created.");
}
尽早删除一个值 std::mem::drop。
fn main() {
let c = CustomSmartPointer {
data: String::from("some data"),
};
println!("CustomSmartPointer created.");
drop(c);
println!("CustomSmartPointer dropped before the end of main.");
}
18.4 使用R进行智能指针计数
fn main() {
let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
println!("count after creating a = {}", Rc::strong_count(&a));
let b = Cons(3, Rc::clone(&a));
println!("count after creating b = {}", Rc::strong_count(&a));
{
let c = Cons(4, Rc::clone(&a));
println!("count after creating c = {}", Rc::strong_count(&a));
}
println!("count after c goes out of scope = {}", Rc::strong_count(&a));
}
18.5 RefCell 和内部可变性模式
#[derive(Debug)]
enum List {
Cons(Rc<RefCell<i32>>, Rc<List>),
Nil,
}
use crate::List::{Cons, Nil};
use std::cell::RefCell;
use std::rc::Rc;
fn main() {
let value = Rc::new(RefCell::new(5));
let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil)));
let b = Cons(Rc::new(RefCell::new(3)), Rc::clone(&a));
let c = Cons(Rc::new(RefCell::new(4)), Rc::clone(&a));
*value.borrow_mut() += 10;
println!("a after = {:?}", a);
println!("b after = {:?}", b);
println!("c after = {:?}", c);
}
18.6 引用可引发内存溢出
n main() {
let leaf = Rc::new(Node {
value: 3,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![]),
});
println!(
"leaf strong = {}, weak = {}",
Rc::strong_count(&leaf),
Rc::weak_count(&leaf),
);
{
let branch = Rc::new(Node {
value: 5,
parent: RefCell::new(Weak::new()),
children: RefCell::new(vec![Rc::clone(&leaf)]),
});
*leaf.parent.borrow_mut() = Rc::downgrade(&branch);
println!(
"branch strong = {}, weak = {}",
Rc::strong_count(&branch),
Rc::weak_count(&branch),
);
println!(
"leaf strong = {}, weak = {}",
Rc::strong_count(&leaf),
Rc::weak_count(&leaf),
);
}
println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
println!(
"leaf strong = {}, weak = {}",
Rc::strong_count(&leaf),
Rc::weak_count(&leaf),
);
}
19. 并发
- 如何创建线程同时运行多段代码
- 消息传递并发,其中通道在线程之间发送消息
- 共享状态并发,其中多个线程可以访问某些数据
- Sync和Send特征,延伸Rust的并发保证由标准库提供的用户定义的类型以及类型
19.1 使用线程运行代码
- 1.使用spawn创建线程
use std::thread;
use std::time::Duration;
fn main() {
thread::spawn(|| {
for i in 1..10 {
println!("hi number {} from the spawned thread!", i);
thread::sleep(Duration::from_millis(1));
}
});
for i in 1..5 {
println!("hi number {} from the main thread!", i);
thread::sleep(Duration::from_millis(1));
}
}
- 2.等待线程结束使用join句柄
use std::thread;
use std::time::Duration;
fn main() {
let handle = thread::spawn(|| {
for i in 1..10 {
println!("hi number {} from the spawned thread!", i);
thread::sleep(Duration::from_millis(1));
}
});
for i in 1..5 {
println!("hi number {} from the main thread!", i);
thread::sleep(Duration::from_millis(1));
}
handle.join().unwrap();
}
如果将执行语句改变,结果也将发生改变:
use std::thread;
use std::time::Duration;
fn main() {
let handle = thread::spawn(|| {
for i in 1..10 {
println!("hi number {} from the spawned thread!", i);
thread::sleep(Duration::from_millis(1));
}
});
handle.join().unwrap();
for i in 1..5 {
println!("hi number {} from the main thread!", i);
thread::sleep(Duration::from_millis(1));
}
}
-
- 在线程中使用move
use std::thread;
fn main() {
let v = vec![1, 2, 3];
let handle = thread::spawn(move || {
println!("Here's a vector: {:?}", v);
});
handle.join().unwrap();
}
19.2 线程直接传递数据
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let val = String::from("hi");
tx.send(val).unwrap();
});
let received = rx.recv().unwrap();
println!("Got: {}", received);
}
管道和所有权转移
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let vals = vec![
String::from("hi"),
String::from("from"),
String::from("the"),
String::from("thread"),
];
for val in vals {
tx.send(val).unwrap();
thread::sleep(Duration::from_secs(1));
}
});
for received in rx {
println!("Got: {}", received);
}
}
19.3 共享状态并发
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("Result: {}", *counter.lock().unwrap());
}
19.4 具有Sync和Send特征的可扩展并发
所述Send标记物性状表示实施类型的值的所有权 Send可以在线程之间转移。几乎所有的 Rust 类型都是Send,但也有一些例外,包括Rc:这不可能是Send因为如果你克隆了一个Rc值并试图将克隆的所有权转移到另一个线程,两个线程可能会同时更新引用计数。出于这个原因,Rc实现用于单线程情况,在这种情况下,您不想支付线程安全的性能损失。
20. Rust的面向对象特性
将展示如何在 Rust 中实现面向对象的设计模式,并讨论这样做与使用 Rust 的一些优势来实现解决方案之间的权衡。
20.1 面向对象语言特点
pub struct AveragedCollection {
list: Vec<i32>,
average: f64,
}
impl AveragedCollection {
pub fn add(&mut self, value: i32) {
self.list.push(value);
self.update_average();
}
pub fn remove(&mut self) -> Option<i32> {
let result = self.list.pop();
match result {
Some(value) => {
self.update_average();
Some(value)
}
None => None,
}
}
pub fn average(&self) -> f64 {
self.average
}
fn update_average(&mut self) {
let total: i32 = self.list.iter().sum();
self.average = total as f64 / self.list.len() as f64;
}
}
20.2 使用允许不同类型值的特征对象
pub trait Draw {
fn draw(&self);
}
pub struct Screen {
pub components: Vec<Box<dyn Draw>>,
}
impl Screen {
pub fn run(&self) {
for component in self.components.iter() {
component.draw();
}
}
}
pub struct Screen<T: Draw> {
pub components: Vec<T>,
}
impl<T> Screen<T>
where
T: Draw,
{
pub fn run(&self) {
for component in self.components.iter() {
component.draw();
}
}
}
pub struct Button {
pub width: u32,
pub height: u32,
pub label: String,
}
impl Draw for Button {
fn draw(&self) {
// code to actually draw a button
}
}
use gui::Draw;
struct SelectBox {
width: u32,
height: u32,
options: Vec<String>,
}
impl Draw for SelectBox {
fn draw(&self) {
// code to actually draw a select box
}
}
use gui::{Button, Screen};
fn main() {
let screen = Screen {
components: vec![
Box::new(SelectBox {
width: 75,
height: 10,
options: vec![
String::from("Yes"),
String::from("Maybe"),
String::from("No"),
],
}),
Box::new(Button {
width: 50,
height: 10,
label: String::from("OK"),
}),
],
};
screen.run();
}
20.3 实现面向对象设计
pub struct Post {
state: Option<Box<dyn State>>,
content: String,
}
impl Post {
pub fn new() -> Post {
Post {
state: Some(Box::new(Draft {})),
content: String::new(),
}
}
}
trait State {}
struct Draft {}
impl State for Draft {}
21. 模式和匹配
- Literals
- Destructured arrays, enums, structs, or tuples
- Variables
- Wildcards
- Placeholders
21.1 所有的地方模式都可以使用
-
- match
match VALUE {
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
}
-
- 条件if let表达式
fn main() {
let favorite_color: Option<&str> = None;
let is_tuesday = false;
let age: Result<u8, _> = "34".parse();
if let Some(color) = favorite_color {
println!("Using your favorite color, {}, as the background", color);
} else if is_tuesday {
println!("Tuesday is green day!");
} else if let Ok(age) = age {
if age > 30 {
println!("Using purple as the background color");
} else {
println!("Using orange as the background color");
}
} else {
println!("Using blue as the background color");
}
}
-
- while let 条件循环
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
while let Some(top) = stack.pop() {
println!("{}", top);
}
-
- for 循环
let v = vec!['a', 'b', 'c'];
for (index, value) in v.iter().enumerate() {
println!("{} is at index {}", value, index);
}
-
- let 声明
let x = 5;
let PATTERN = EXPRESSION;
let (x, y, z) = (1, 2, 3);
-
- 功能参数
fn foo(x: i32) {
// code goes here
}
21.2 匹配和不匹配
if let Some(x) = some_option_value {
println!("{}", x);
}
if let x = 5 {
println!("{}", x);
};
20.3 模式语法
-
- 匹配文字
let x = 1;
match x {
1 => println!("one"),
2 => println!("two"),
3 => println!("three"),
_ => println!("anything"),
}
-
- 匹配命名变量
let x = Some(5);
let y = 10;
match x {
Some(50) => println!("Got 50"),
Some(y) => println!("Matched, y = {:?}", y),
_ => println!("Default case, x = {:?}", x),
}
println!("at the end: x = {:?}, y = {:?}", x, y);
-
- 多种模式
let x = 1;
match x {
1 | 2 => println!("one or two"),
3 => println!("three"),
_ => println!("anything"),
}
-
- 匹配值的范围 ..=
数字:
let x = 5;
match x {
1..=5 => println!("one through five"),
_ => println!("something else"),
}
字符:
let x = 'c';
match x {
'a'..='j' => println!("early ASCII letter"),
'k'..='z' => println!("late ASCII letter"),
_ => println!("something else"),
}
-
- 解构以打破价值观
解构结构:
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 0, y: 7 };
let Point { x: a, y: b } = p;
assert_eq!(0, a);
assert_eq!(7, b);
let p = Point { x: 0, y: 7 };
match p {
Point { x, y: 0 } => println!("On the x axis at {}", x),
Point { x: 0, y } => println!("On the y axis at {}", y),
Point { x, y } => println!("On neither axis: ({}, {})", x, y),
}
}
解构枚举:
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
fn main() {
let msg = Message::ChangeColor(0, 160, 255);
match msg {
Message::Quit => {
println!("The Quit variant has no data to destructure.")
}
Message::Move { x, y } => {
println!(
"Move in the x direction {} and in the y direction {}",
x, y
);
}
Message::Write(text) => println!("Text message: {}", text),
Message::ChangeColor(r, g, b) => println!(
"Change the color to red {}, green {}, and blue {}",
r, g, b
),
}
}
解构嵌套结构和枚举:
enum Color {
Rgb(i32, i32, i32),
Hsv(i32, i32, i32),
}
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(Color),
}
fn main() {
let msg = Message::ChangeColor(Color::Hsv(0, 160, 255));
match msg {
Message::ChangeColor(Color::Rgb(r, g, b)) => println!(
"Change the color to red {}, green {}, and blue {}",
r, g, b
),
Message::ChangeColor(Color::Hsv(h, s, v)) => println!(
"Change the color to hue {}, saturation {}, and value {}",
h, s, v
),
_ => (),
}
}
解构结构和元组:
let ((feet, inches), Point { x, y }) = ((3, 10), Point { x: 3, y: -10 });
-
- 忽略模式中的值
忽略整个值 _
fn foo(_: i32, y: i32) {
println!("This code only uses the y parameter: {}", y);
}
fn main() {
foo(3, 4);
}
使用嵌套忽略部分值 _
let mut setting_value = Some(5);
let new_setting_value = Some(10);
match (setting_value, new_setting_value) {
(Some(_), Some(_)) => {
println!("Can't overwrite an existing customized value");
}
_ => {
setting_value = new_setting_value;
}
}
println!("setting is {:?}", setting_value);
let numbers = (2, 4, 8, 16, 32);
match numbers {
(first, _, third, _, fifth) => {
println!("Some numbers: {}, {}, {}", first, third, fifth)
}
}
忽略一个未使用的变量,以它的名字开头 _
fn main() {
let _x = 5;
let y = 10;
}
忽略值的剩余部分 ..
struct Point {
x: i32,
y: i32,
z: i32,
}
let origin = Point { x: 0, y: 0, z: 0 };
match origin {
Point { x, .. } => println!("x is {}", x),
}
如何使用..元组
fn main() {
let numbers = (2, 4, 8, 16, 32);
match numbers {
(first, .., last) => {
println!("Some numbers: {}, {}", first, last);
}
}
}
试图以..一种模棱两可的方式使用的错误使用:
fn main() {
let numbers = (2, 4, 8, 16, 32);
match numbers {
(.., second, ..) => {
println!("Some numbers: {}", second)
},
}
}
带有匹配守卫的额外条件
let num = Some(4);
match num {
Some(x) if x < 5 => println!("less than five: {}", x),
Some(x) => println!("{}", x),
None => (),
}
@ 绑定
enum Message {
Hello { id: i32 },
}
let msg = Message::Hello { id: 5 };
match msg {
Message::Hello {
id: id_variable @ 3..=7,
} => println!("Found an id in range: {}", id_variable),
Message::Hello { id: 10..=12 } => {
println!("Found an id in another range")
}
Message::Hello { id } => println!("Found some other id: {}", id),
}
22 高级功能
- 不安全的Rust
- 高级特性
- 高级类型
- 高级函数和闭包
- 宏指令
22.1 不安全的Rust
unsafe trait Foo {
// methods go here
}
unsafe impl Foo for i32 {
// method implementations go here
}
fn main() {}
22.2 高级特性
use std::fmt;
struct Wrapper(Vec<String>);
impl fmt::Display for Wrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}]", self.0.join(", "))
}
}
fn main() {
let w = Wrapper(vec![String::from("hello"), String::from("world")]);
println!("w = {}", w);
}
22.3 高级类型
-
- 创建匿名类型
type Kilometers = i32;
let x: i32 = 5;
let y: Kilometers = 5;
println!("x + y = {}", x + y);
-
- 从未使用类型和返回值
impl<T> Option<T> {
pub fn unwrap(self) -> T {
match self {
Some(val) => val,
None => panic!("called `Option::unwrap()` on a `None` value"),
}
}
}
22.4 高级函数和闭包
fn add_one(x: i32) -> i32 {
x + 1
}
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
f(arg) + f(arg)
}
fn main() {
let answer = do_twice(add_one, 5);
println!("The answer is: {}", answer);
}
fn returns_closure() -> Box<dyn Fn(i32) -> i32> {
Box::new(|x| x + 1)
}
22.5 宏指令
展示一个简单的定义vec!宏
#[macro_export]
macro_rules! vec {
( $( $x:expr ),* ) => {
{
let mut temp_vec = Vec::new();
$(
temp_vec.push($x);
)*
temp_vec
}
};
}
23 最终的项目:创建一个多线程的Web服务
-
学习一点TCP和HTTP
-
TCP的Stocket监听
-
在HTTP请求中处理一个数字
-
创建一个HTTP反馈
-
改进Web服务提供线程池功能
23.1 创建一个单线程的Web服务
use std::net::TcpListener;
fn main() {
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
for stream in listener.incoming() {
let stream = stream.unwrap();
println!("Connection established!");
}
}
23.2 将单线程转变为多线程
use std::thread;
use std::time::Duration;
// --snip--
fn handle_connection(mut stream: TcpStream) {
// --snip--
let get = b"GET / HTTP/1.1\r\n";
let sleep = b"GET /sleep HTTP/1.1\r\n";
let (status_line, filename) = if buffer.starts_with(get) {
("HTTP/1.1 200 OK", "hello.html")
} else if buffer.starts_with(sleep) {
thread::sleep(Duration::from_secs(5));
("HTTP/1.1 200 OK", "hello.html")
} else {
("HTTP/1.1 404 NOT FOUND", "404.html")
};
// --snip--
}
创建线程池对象:
use std::thread;
pub struct ThreadPool {
workers: Vec<Worker>,
}
impl ThreadPool {
// --snip--
pub fn new(size: usize) -> ThreadPool {
assert!(size > 0);
let mut workers = Vec::with_capacity(size);
for id in 0..size {
workers.push(Worker::new(id));
}
ThreadPool { workers }
}
// --snip--
}
struct Worker {
id: usize,
thread: thread::JoinHandle<()>,
}
impl Worker {
fn new(id: usize) -> Worker {
let thread = thread::spawn(|| {});
Worker { id, thread }
}
}
23.3 跟踪和清理工作
impl Worker {
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
// --snip--
Worker {
id,
thread: Some(thread),
}
}
}
清理:
impl Drop for ThreadPool {
fn drop(&mut self) {
for worker in &mut self.workers {
println!("Shutting down worker {}", worker.id);
if let Some(thread) = worker.thread.take() {
thread.join().unwrap();
}
}
}
}
看完了,但是真正的使用上,感觉还需要一段时间呀!
路漫漫及其修远,吾将上下而求索。
2021年06月15日