github email
LearnRust

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);
}
    1. 代码定义
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

    1. 进行有用的文档评论

文档注释使用三个斜杠 ,///而不是两个,并且支持 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
}
    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
    1. 发布到 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)
    1. 从 Crates.io 中删除版本 cargo yank
$ cargo yank --vers 1.0.1
$ cargo yank --vers 1.0.1 --undo

17.3 Cargo工作区域

    1. 创建工作区域
$ 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
    1. 在工作区域中创建第二个包
[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
    1. 在工作区域中添加依赖包
[dependencies]
rand = "0.8.3"
    1. 在工作区域中添加测试
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));
    }
}
    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 所有的地方模式都可以使用

    1. match
match VALUE {
    PATTERN => EXPRESSION,
    PATTERN => EXPRESSION,
    PATTERN => EXPRESSION,
}
    1. 条件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");
    }
}
    1. while let 条件循环
    let mut stack = Vec::new();

    stack.push(1);
    stack.push(2);
    stack.push(3);

    while let Some(top) = stack.pop() {
        println!("{}", top);
    }
    1. for 循环
    let v = vec!['a', 'b', 'c'];

    for (index, value) in v.iter().enumerate() {
        println!("{} is at index {}", value, index);
    }
    1. let 声明
let x = 5;
let PATTERN = EXPRESSION;
let (x, y, z) = (1, 2, 3);
    1. 功能参数
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 模式语法

    1. 匹配文字
 let x = 1;

    match x {
        1 => println!("one"),
        2 => println!("two"),
        3 => println!("three"),
        _ => println!("anything"),
    }
    1. 匹配命名变量
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);
    1. 多种模式
let x = 1;

    match x {
        1 | 2 => println!("one or two"),
        3 => println!("three"),
        _ => println!("anything"),
    }
    1. 匹配值的范围 ..=

数字:

    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"),
    }
    1. 解构以打破价值观

解构结构:

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 });
    1. 忽略模式中的值

忽略整个值 _

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 高级类型

    1. 创建匿名类型
    type Kilometers = i32;

    let x: i32 = 5;
    let y: Kilometers = 5;

    println!("x + y = {}", x + y);
    1. 从未使用类型和返回值
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日