Rust学习笔记

The Rust Programming Language学习笔记

文章目录

1 开始

1.1 你好,世界

fn main() {
    println!("Hello, world!");
}
# 构建
rustc main.rs
# 运行
./main
  • 缩进
    rust的缩进是4个空格

  • 宏(macro)和函数(function)
    println!是一个宏,println是一个函数,区别是后面有没有加感叹号

  • 表达式结束后加分号;

1.2 你好,cargo

Cargo is Rust’s build system and package manager.

cargo --version | -V

# 查看cargo版本
cargo --version
# 创建新项目
cargo new hello_cargo
# 构建和运行cargo项目
cargo build
./target/debug/hello-cargo
# 直接运行cargo项目
cargo run 
# 快速检查代码是否能通过编译
cargo check
# 构建发行版本
cargo build --release

cargo中代码包被称为crates

Cargo.lock会自动跟踪项目依赖的版本,cargo会自动管理这个文件。

cargo发行版本与debug版的区别,发行版本添加优化,构建更慢,运行更快。

2 编写一个猜谜游戏

2.1 版本1

use std::io; // 导入io库

fn main() {
    println!("Guess the number!");

    println!("Please input your guess.");

    // 使用let创建变量,rust中变量默认是不可变的,加上mut后可变。 String是标准库提供的类型。
    let mut guess = String::new(); // `String::new()` 表明`new`是String类型自带的方法。
    // 这个语句创建了一个可变变量, 绑定了一个新的、空的String实例。

    // 每行一个方法是Rust推荐的
    io::stdin() // 调用io模块的`stdin`函数,返回std::io::Stdin的一个实例,是一个代表处理终端输入的类型
        .read_line(&mut guess) // 实例调用read_line方法;`&`表示参数是一个引用,作用是避免拷贝。
        .expect("Failed to read line"); // read_line会返回一个`Result`,`Result`的结果可能是Ok,或Err。 
        // `Result`的返回值需要得到处理。 如果值是Err会打印expect中的message。
    
    println!("Your guessed: {}", guess); // 占位符(placeholder)
}

2.2 版本2 加随机数

产生随机数

需要先在Cargo.toml中加入 rand的依赖

[dependencies]是一个section header

The number 0.5.5 is actually shorthand for ^0.5.5, which means “any version that has a public API compatible with version 0.5.5.”

[dependencies]
rand = "0.5.5"

cargo build自动下载相关依赖,第一次cargo build的时候,Cargo.lock会锁定相关依赖,之后的build不会再计算依赖的版本。
cargo update可以更新Cargo.lock中锁定的依赖版本。cargo update默认不会更新大版本,如0.5.0可以更新到0.5.9
但是不能更新到0.6.0
如果需要更新0.6.x需要在cargo.toml中指定为0.6.0

cargo doc --open用于查看所有依赖的文档

use std::io; // 导入io库
use rand::Rng;
use std::cmp::Ordering; // Ordering是类型, 是另一种enum类型

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");  

    // 讲string类型的guess转换为 u32; Rust允许屏蔽变量,即用同一个变量名创建一个新的变量,前面的变量会被屏蔽。
    let guess: u32 = guess.trim().parse().expect("Please type a number!");
    // trim()的作用是消除whitespace
    // parse()的作用是将字符串解析为某种数字
    
    println!("Your guessed: {}", guess); 

    // match 类似switch case
    match guess.cmp(&secret_number) {
        Ordering::Less => println!("Too small!"),
        Ordering::Greater => println!("Too big!"),
        Ordering::Equal => println!("You win!"),
    }
}

2.3 版本3 加循环

use std::io; // 导入io库
use rand::Rng;
use std::cmp::Ordering; // Ordering是类型, 是另一种enum类型

fn main() {
    println!("Guess the number!");

    let secret_number = rand::thread_rng().gen_range(1, 101); // 左闭右开

    // println!("The secret number is: {}", secret_number);

    loop {
        println!("Please enter your guess."); 

        let mut guess = String::new(); 
        io::stdin() 
        .read_line(&mut guess) 
        .expect("Failed to read line");  

        // let guess: u32 = guess.trim().parse().expect("Please type a number!");
        // parse会返回一个Result, Result是一个enum, 包含变量Ok和Err. 
        // parse成功时,parse出的数字会包含在Ok中。Ok(num), num只是一个名字,可以是其他名字。
        // parse失败时,返回Err,Err内包含了更多的信息, _表示 catch all. 
        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => continue,
        };

        match guess.cmp(&secret_number) {
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => {
                println!("You win!");
                break;
            }
        }
    }

}

3 通用编程概念

3.1 变量和可变性

rust变量默认是不可变的

让变量可变需要加上mut

3.2 变量和常量的区别

const而不是let定义常量,并且常量的类型必须声明,不能用变量或函数初始化常量。

RUST的常量命名示例:MAX_POINTS

const MAX_POINTS: u32 = 1000_000;

3.3 屏蔽(Shadowing)

We can shadow a variable by using the same
variable’s name and repeating the use of the let keyword as follows.

fn main() {
    let x = 5;
    let x = x + 1;
    let x = x * 2;
    println!("The value of x is:{}", x);
}

修改变量类型

let spaces = "    "
let spaces = spaces.len();

3.4 数据类型

静态类型语言:在编译时就知道所有变量的类型。

3.4.1 Scalar Types

represents a single value.

Rust有四种主要的scalar types: integers, floating-points numbers, Booleans, and characters.

3.4.1.1 Integer Types

An integer is a number without a fractional component.

The default type is i32

3.4.1.2 Floating-Points Types

f32 and f64

The default type is f64 because on modern CPUs it’s roughly the same speed as f32 but is capable of more precision.

3.4.1.3 The Boolean Type
fn main() {
    let t = true;

    let f: bool = false; // with explicit type annotation
}
3.4.1.4 The Character Type

rust的char类型占4个字节,表示Unicode Scalar Value.

char specified with 单引号(single quotes)

string literals with 双引号(double quotes)

3.4.2 Compound Types

Compound Types can group multiple values into one type.

Rust有两种primitive compound types: tuples and arrays.

3.4.2.1 元组类型(The Tuple Type)

A tuple is a general way of grouping together a number of values with a variety of types into one compound type.

Tuples have a fixed length: once declared, they cannot grow or shrink in size.

例子1:

fn main() {
    let tup: (i32, f64, u8) = (500, 6.4, 1);
}

例子2:

fn main() {
    let tup: (i32, f64, u8) = (500, 6.4, 1);
    let tup = (500, 6.4, 1);
    let (x, y, z) = tup;
    println!("The value of y is: {}", y);
}

例子3:

fn main() {
    let x: (i32, f64, u8) = (500, 6.4, 1);

    let five_houdred = x.0;

    let six_point_four = x.1;

    let one = x.2;
}
3.4.2.2 数组类型(The Array Type)

Arrays in Rust have a fixed length.

数组是在栈上的。

例子1:

fn main() {
    let a = [1, 2, 3, 4, 5];

    let b: [i32; 5] = [1, 2, 3, 4, 5];

    // 5个值,每个都为3.
    let a = [3; 5];
}

3.5 函数

Rust code uses snake case as the conventional style for function and variable names.

In snake case, all letters are lowercase and underscores separate words.

例子1:

fn main() {
    println!("Hello, world!");

    another_function();
}

fn another_function() {
    println!("another_function.");
}

3.5.1 函数参数

fn main() {
    another_function(5);
    another_function2(6, 7);
}

fn another_function(x: i32) {
    println!("The value of x is: {}", x);
}

fn another_function2(y: i32, z: i32) {
    println!("The value of y is: {}", y);
    println!("The value of z is: {}", z);
}
3.5.1.1 语句和表达式

语句是完成特定动作的指令,不会返回值。

Statements are instructions that perform some action and do not return a value.

表达式会返回值。

Expressions evaluate to a resulting value.

fn main() {
    let x = 5;

    let y = {
        let x = 3;
        x + 1
    };

    println!("The value of y is: {}", y);
}
3.5.1.2 带有返回值的函数

最后一个表达式默认为返回值,也可以用return来返回。

Note: 带有分号的是语句,不是表达式。

例子:

fn five() -> i32 {
    5
}

fn six() -> i32 {
    return 6;
}

fn main() {
    let x = five();

    println!("The value of x is: {}", x);

    let x = six();

    println!("The value of x is: {}", x);
}

3.6 控制流

3.6.1 if 表达式

例子1:

fn main() {
    let number = 6;

    if number % 4 == 0 {
        
    } else if number % 3 == 0 {

    } else {

    }
}

例子2:

fn main() {
    let condition = true;
    let number = if condition { 5 } else { 6 }; // Note: if 和 else中的value必须是compatable的。
    println!("The value of number is: {}", number);
}

3.6.2 循环

Rust有三种类型的循环: loop,while, and for

例子1:

fn main() {
    loop {
        println!("again!");
    }
}

例子2:

fn main() {
    let mut counter = 0;

    let result = loop {
        counter += 1;

        if counter == 10 {
            break counter * 2;
        }
    };

    println!("The result is {}", result);
}

例子3:

fn main() {
    let mut number = 3;

    while number != 0 {
        println!("{}!", number);

        number -= 1;
    }

    println!("LIFTOFF!!!");
}

例子4:

fn main() {
    let a = [10, 20, 30, 40, 50];
    let mut index = 0;

    while index < 5 {
        println!("the value is: {}", a[index]);

        index += 1;
    }
}

例子5:

fn main() {
    let a = [10, 20, 30, 40, 50];

    for element in a.iter() {
        println!("the value is: {}", element);
    }
}

例子6:

fn main() {
    for number in (1..4).rev() { // rev is reverse  左闭右开
        println!("{}!", number);
    }
    println!("LIFTOFF!!!");
}

4 理解所有权 (Understand Ownership)

Rust uses a third approach: memory is managed through a system of ownership
with a set of rules that the compiler checks at compile time.

栈和堆

存储在栈中的data的大小必须是固定的,已知的。

栈比堆要快。

Keeping track of what parts of code are using what data on the heap, minimizing the
amount of duplicate data on the heap, and cleaning up unused data on the heap so
you don’t run out of space are all problems that ownership addresses

4.1 什么是Ownership

4.1.1 归属规则(Ownership Rules)

  • Rust中每个值都归属于一个变量

  • 同一时间只有一个所有者

  • 当所有者离开作用域,这个值会被丢弃

4.1.2 变量作用域

例子1:

fn main() {
    {                      // s is not valid here, it’s not yet declared
        let s = "hello";   // s is valid from this point forward

        // do stuff with s
    }                      // this scope is now over, and s is no longer valid
}

4.1.3 The String Type

字符串字面量是不可变的。

字符串类型。

fn main() {
    let s = String::from("hello");

    let mut s = String::from("hello");
    
    s.push_str(", world!");
    
    println!("{}", s);
}

4.1.4 Memory and Allocation

when s goes out of scope. When a variable goes out of scope, Rust calls a special function for us. This function is called drop, and it’s where the author of String can put the code to return the memory. Rust calls drop automatically at the closing curly bracket.

4.1.5 Ways Variables and Data Interact:Move

let x = 5;
let y = x;

x, y 都是存储在栈上的,所以都Ok.

let s1 = String::from("hello");
let s2 = s1; 

s1是存储在堆上的, s2 = s1, 只是复制了字符串信息(存储在栈上的),没有复制底层字符串数组(存储在堆上的)(复制底层数组代价太高)。 类似于浅拷贝。

当变量走出作用域时,Rust会调用drop来清理内存。为了保证不会两次清理同一片内存,Rust的做法是,当s2 = s1之后,s1就失效了。 这种做法叫做move.

s2 = s1; 也可以说是 s1 was moved into s2.

4.1.6 Ways Variables and Data Interact: Clone

如果需要深拷贝。

let s1 = String::from("hello");
let s2 = s1.clone();

println!("s1 = {}, s2 = {}", s1, s2);

4.1.7 Stack-Only Data: Copy

fn main() {
    let x = 5;
    let y = x;

    println!("x = {}, y = {}", x, y);
}

只存储在栈上的数据类型,不存在深拷贝。 所以复制后,两个值都是有效的。

这种类型有一个Copy trait。带有Copy trait的类型,不能有Drop trait.

这种类型有:

  • 所有的整数类型
  • 所有的布尔类型
  • 所有的浮点类型
  • char类型
  • 元组类型(tuples)【只包含Copy类型的元组类型】

4.1.8 所有权和函数

函数传参 也会发生move或者copy,就像赋值发生的事情一样。

fn main() {
    let s = String::from("hello");  // s comes into scope

    takes_ownership(s);             // s's value moves into the function...
                                    // ... and so is no longer valid here

    let x = 5;                      // x comes into scope

    makes_copy(x);                  // x would move into the function,
                                    // but i32 is Copy, so it’s okay to still
                                    // use x afterward

} // Here, x goes out of scope, then s. But because s's value was moved, nothing
  // special happens.

fn takes_ownership(some_string: String) { // some_string comes into scope
    println!("{}", some_string);
} // Here, some_string goes out of scope and `drop` is called. The backing
  // memory is freed.

fn makes_copy(some_integer: i32) { // some_integer comes into scope
    println!("{}", some_integer);
} // Here, some_integer goes out of scope. Nothing special happens.

4.1.9 返回值和作用域

返回值也会转移所有权。

fn main() {
    let s1 = gives_ownership();         // gives_ownership moves its return
                                        // value into s1

    let s2 = String::from("hello");     // s2 comes into scope

    let s3 = takes_and_gives_back(s2);  // s2 is moved into
                                        // takes_and_gives_back, which also
                                        // moves its return value into s3
} // Here, s3 goes out of scope and is dropped. s2 goes out of scope but was
  // moved, so nothing happens. s1 goes out of scope and is dropped.

fn gives_ownership() -> String {             // gives_ownership will move its
                                             // return value into the function
                                             // that calls it

    let some_string = String::from("hello"); // some_string comes into scope

    some_string                              // some_string is returned and
                                             // moves out to the calling
                                             // function
}

// takes_and_gives_back will take a String and return one
fn takes_and_gives_back(a_string: String) -> String { // a_string comes into
                                                      // scope

    a_string  // a_string is returned and moves out to the calling function
}

返回多个值

fn main() {
    let s1 = String::from("hello");

    let (s2, len) = calculate_length(s1);

    println!("The length of '{}' is {}.", s2, len);
}

fn calculate_length(s: String) -> (String, usize) {
    let length = s.len(); // len() returns the length of a String

    (s, length)
}

4.2 引用和借用(Reference and Borrowing)

函数也会move是不是很不方便,可以用引用来解决这个问题。

例子:

fn main() {
    let s1 = String::from("hello");

    let len = calculate_length(&s1);

    println!("The length of '{}' is {}.", s1, len);
}

fn main() {
    let s1 = String::from("hello");

    let len = calculate_length(&s1);

    println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: &String) -> usize { // s is a reference to a String
    s.len()
} // Here, s goes out of scope. But because it does not have ownership of what
  // it refers to, nothing happens.

引用

We call having references as function parameters borrowing.

4.2.1 可变引用

【避免 data race】
可变引用有一个限制,在特定作用域中,对于特定数据只能有一个可变引用。

fn main() {
    let mut s = String::from("hello");

    change(&mut s);
}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

不能同时对一个数据,使用可变引用和不可变引用。

但值得注意的是:引用的作用域是从 引入引用的地方开始的。

看下面的例子,最后一次使用不可变引用是在引入可变引用之前,这是没有问题的。

fn main() {
    let mut s = String::from("hello");

    let r1 = &s; // no problem
    let r2 = &s; // no problem
    println!("{} and {}", r1, r2);
    // r1 and r2 are no longer used after this point

    let r3 = &mut s; // no problem
    println!("{}", r3);
}

4.2.2 悬挂引用(Dangling References)

什么是悬挂引用:
a pointer that references a location in memory that may have been given to someone else, by freeing some memory while preserving a pointer to that memory.

Rust的悬挂引用会在编译阶段报错。

fn main() {
   let reference_to_nothing = dangle();
}

fn dangle() -> &String {
   let s = String::from("hello");

   &s
}

返回了一个局部变量的引用,局部变量离开作用域后就会消失,那么这个引用就成了悬挂引用。

解决方案是:

fn main() {
   let string = no_dangle();
}

fn no_dangle() -> String {
   let s = String::from("hello");

   s
}

4.3 切片类型(Slice Type)

slice是一种没有所有权的类型。

a string slice

fn main() {
    let s = String::from("hello world");

    let hello = &s[0..5];
    let world = &s[6..11];
}

string切片

let slice = &s[0..2];let slice = &s[..2];是等价的

let slice = &s[0..s.len()];let slice = &s[..]是等价的

返回第一个word的函数:

fn first_word(s: &String) -> &str {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }

    &s[..]
}

4.3.1 字符串字面量是切片(String Literals Are Slices)

4.3.2 字符串切片作为参数

fn first_word(s: &String) -> &str { // 字符串引用作为参数,不能传入切片
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }

    &s[..]
}

fn first_word(s: &str) -> &str { // 字符串切片作为参数,更可取
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }

    &s[..]
}

4.3.3 其它的切片


#![allow(unused_variables)]
fn main() {
let a = [1, 2, 3, 4, 5];

let slice = &a[1..3];
}

5 使用Structs实例化关联数据

5.1 定义和实例化结构体

结构的所有field都必须是mutable,Rust不允许部分field mutable。

例子:

struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

let mut user1 = User {
    email: String::from("someone@example.com"),
    username: String::from("someusername123"),
    active: true,
    sign_in_count: 1,
};


user1.email = String::from("anotheremail@example.com");

fn build_user(email: String, username: String) -> User {
    User {
        email: email,
        username: username,
        active: true,
        sign_in_count: 1,
    }
}

// 参数和field名字相同时,可以用的简便初始化方式
fn build_user(email: String, username: String) -> User {
    User {
        email,
        username,
        active: true,
        sign_in_count: 1,
    }
}

// 用原有的结构实例去创建新的实例
let user2 = User {
    email: String::from("another@example.com"),
    username: String::from("anotherusername567"),
    active: user1.active,
    sign_in_count: user1.sign_in_count,
}

// 用结构更新语法简化
let user3 = User {
    email: String::from("another@example.com"),
    username: String::from("anotherusername567"),
    ..user1
}

5.1.1 使用元组创建不同的类型

fn main() {
    struct Color(i32, i32, i32);
    struct Point(i32, i32, i32);

    let black = Color(0, 0, 0);
    let origin = Point(0, 0, 0);
}

5.2 一个使用结构体的例程

版本1:

fn main() {
    let width1 = 30;
    let height1 = 50;

    println!(
        "The area of the rectangle is {} square pixels.",
        area(width1, height1)
    );
}

fn area(width: u32, height: u32) -> u32 {
    width * height
}

版本2:

fn main() {
    let rect1 = (30, 50);

    println!(
        "The area of the rectangle is {} square pixels.",
        area(rect1)
    );
}

fn area(dimensions: (u32, u32)) -> u32 {
    dimensions.0 * dimensions.1
}

版本3:

struct Rectangle {
    width: u32,
    height: u32,
}

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };

    println!(
        "The area of the rectangle is {} square pixels.",
        area(&rect1)
    );
}

fn area(rectangle: &Rectangle) -> u32 {
    rectangle.width * rectangle.height
}

5.2.1 添加具有衍生特征的有用功能

要直接打印结构体的话,需要加上#[derive(Debug)]这个trait

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };

    println!("rect1 is {:?}", rect1);
}

5.3 方法语法

例子:

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }

    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}

fn main() {
    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };
    let rect2 = Rectangle {
        width: 10,
        height: 40,
    };
    let rect3 = Rectangle {
        width: 60,
        height: 45,
    };
    
    println!(
        "The area of the rectangle is {} square pixels.",
        rect1.area()
    );
    println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
    println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
}

5.3.1 关联函数

可以在impl中定义不把自身作为参数的函数。它们叫做关联函数,String::from就是关联函数。

例子:

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn square(size: u32) -> Rectangle {
        Rectangle {
            width: size,
            height: size,
        }
    }
}

fn main() {
    let sq = Rectangle::square(3);
}

5.3.2 多个impl Blocks

每个结构体都允许多个Blocks

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

impl Rectangle {
    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}

6 枚举与模式匹配

6.1 定义枚举

Ip地址可以是V4,或者V6,但不会同时是V4和V6。

enum类型可以做到这一点。

enum IpAddrKind {
    V4,
    V6,
}

6.1.1 Enum的值

IpAddrKind::V4IpAddrKind::V6都是IpAddrKind类型

let four = IpAddrKind::V4;
let six = IpAddrKind::V6;

版本1:

fn main() {
    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"),
    };
}

版本2:

fn main() {
    enum IpAddr {
        V4(String),
        V6(String),
    }

    let home = IpAddr::V4(String::from("127.0.0.1"));

    let loopback = IpAddr::V6(String::from("::1"));
}

There’s another advantage to using an enum rather than a struct: each variant can have different types and amounts of associated data。

fn main() {
    enum IpAddr {
        V4(u8, u8, u8, u8),
        V6(String),
    }

    let home = IpAddr::V4(127, 0, 0, 1);

    let loopback = IpAddr::V6(String::from("::1"));
}

上面的enum和下面的4个结构体的作用是相同的。

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}
 
struct QuitMessage; // unit struct
struct MoveMessage {
    x: i32,
    y: i32,
}
struct WriteMessage(String); // tuple struct
struct ChangeColorMessage(i32, i32, i32); // tuple struct

enum类型也可以向结构体一样定义方法

fn main() {
    enum Message {
        Quit,
        Move { x: i32, y: i32 },
        Write(String),
        ChangeColor(i32, i32, i32),
    }

    impl Message {
        fn call(&self) {
            // method body would be defined here
        }
    }

    let m = Message::Write(String::from("hello"));
    m.call();
}

6.1.2 Option Enum 及其好处

Option是标准库中定义的另一种Enum

scenario in which a value could be something or it could be nothing.

Rust没有Null值, Rust用Option来表示空值。

如果这个变量可能是空值,那么需要通过Option来显式声明。

这就意味着除此之外的变量都不可能是空值,可以放心使用。

enum Option<T> {
    Some(T),
    None,
}

你不需要将Option<T>显式地引入作用域,you can use Some and None directly without the Option:: prefix。

例子:

如果使用None,需要显式指定Option<T>的类型

fn main() {
    let some_number = Some(5);
    let some_string = Some("a string");

    let absent_number: Option<i32> = None; // 如果使用`None`,需要显式指定Option<T>的类型
}

Option<T>类型和T类型是不同的类型,不能直接运算。

fn main() {
    let x: i8 = 5;
    let y: Option<i8> = Some(5);

    let sum = x + y;
}

6.2 match控制流符号

The power of match comes from the expressiveness of the patterns and the fact that the compiler confirms that all possible cases are handled.

match的每个分支,用=>来分割模式表达式

Note: 表达式返回的是值,是可以放在等号右边的。

match的分支必须匹配每一种情况,否则会报错。

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,
    }
}

6.2.1 模式绑定值 …

6.2.3 Match with Option

fn main() {
    fn plus_one(x: Option<i32>) -> Option<i32> {
        match x {
            None => None,
            Some(i) => Some(i + 1),
        }
    }

    let five = Some(5);
    let six = plus_one(five);
    let none = plus_one(None);
}

6.2.4 _占位符

_ will match any value.

6.3 简洁控制流 if let

可以将if let视为 match的语法糖。

let some_u8_value = Some(0u8);
match some_u8_value {
    Some(3) => println!("three"),
    _ => (),
}
if let Some(3) = some_u8_value {
    println!("three");
}

7 用包、cratesmodules来管理增长的项目 (待续)

  • 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

7.1 包和 Crates

rust用命名空间来避免命名冲突

7.2 定义Modules 来控制scope和privacy

  • use: brings a path into scope.
  • pub: make items public.
  • as: external packages, and the glob operator.

模块可以将crates分组,提高可读性和重用性。

7.3 Paths for Referring to an Item in the Module Tree

A path can take two forms:

  • An absolute path starts from a crate root by using a crate name or a literal crate.
  • A relative path starts from the current module and uses self, super, or an identifier in the current module.

8 通用集合(Common Collections)

8.1 vector

vector存储在heap中,支持push和pop。更多操作参考API文档。

8.1.1 创建新的vector

let v: Vec<i32> = Vec::new();

很多时候,rust可以自动推断出类型.

可以使用vec!来简化vector的初始化。

let v = vec![1, 2, 3];

8.1.2 更新vector

let mut v = Vec::new(); // 这时候不知道类型

v.push(5); // 自动推断为i32
v.push(6);
v.push(7);
v.push(8);

8.1.3 Dropping a Vector Drops Its Elements

Like any other struct, a vector is freed when it goes out of scope。

{
    let v = vec![1, 2, 3, 4];

    // do stuff with v
} // <- v goes out of scope and is freed here

8.1.4 读取vector中的元素

rust的两种读取元素的方式:

fn main() {
    let v = vec![1, 2, 3, 4, 5];

    let third: &i32 = &v[2];
    println!("The third element is {}", third);

    match v.get(2) {
        Some(third) => println!("The third element is {}", third),
        None => println!("There is no third element."),
    }
}

rust不支持同时mut和不mut的引用

fn main() {
    let mut v = vec![1, 2, 3, 4, 5]; 

    let first = &v[0]; // not mutable

    v.push(6); // mutable -> err

    println!("The first element is: {}", first);
}

vector新增元素,导致不可变引用出错? 原因是当cap满了的时候,可能重新分配存储空间。

8.1.5 遍历 vector

不可变遍历

fn main() {
    let v = vec![100, 32, 57];
    for i in &v {
        println!("{}", i);
    }
}

可变遍历

fn main() {
    let mut v = vec![100, 32, 57];
    for i in &mut v {
        *i += 50;
    }
}

*是dereference operator

8.1.6 使用Enum存储Multiple Types

fn main() {
    enum SpreadsheetCell {
        Int(i32),
        Float(f64),
        Text(String),
    }

    let row = vec![
        SpreadsheetCell::Int(3),
        SpreadsheetCell::Text(String::from("blue")),
        SpreadsheetCell::Float(10.12),
    ];
}

Rust needs to know what types will be in the vector at compile time so it knows exactly how much memory on the heap will be needed to store each element.

8.2 用Strings存储UTF-8编码的文本

8.2.1 什么是String

Rust has only one string type in the core language, which is the string slice str that is usually seen in its borrowed form &str.

string slices, which are references to some UTF-8 encoded string data stored elsewhere.

String literals, for example, are stored in the program’s binary and are therefore string slices.

The String type, which is provided by Rust’s standard library rather than coded into the core language, is a growable, mutable, owned, UTF-8 encoded string type.

When Rustaceans refer to “strings” in Rust, they usually mean the String and the string slice &str types, not just one of those types.

8.2.2 创建一个新String

let mut s = String::new();

we use the to_string method, which is available on any type that implements the Display trait.

fn main() {
    let data = "initial contents";

    let s = data.to_string();

    // the method also works on a literal directly:
    let s = "initial contents".to_string();

    let s = String::from("initial contents");
}

strings是UTF-8 encoded, 所以

fn main() {
    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");
}

8.2.3 更新String

String的size和内容是可以改变的。

8.2.3.1 push_strpush
let mut s = String::from("foo");
s.push_str("bar"); // push_str takes string slice
let mut s = String::from("lo");
s.push('l'); // push takes a single letter
8.2.3.2 Concatenation with the + Operation or the format Macro
let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2; // note s1 has moved here and can no longer be used.

s2使用引用的原因是,使用+ operator. + operator使用了 add方法, add方法的签名(库中的add是用泛型实现的)如下所示:

the compiler can coerce(胁迫) the &String argument into a &str

here turns &s2 into &s2[…].

fn add(self, s: &str) -> String {} // self没有加 & , 那意味着self的所有权将进入add, 那么s1将会失效。

复杂的combining,可以使用format!

fn main() {
    let s1 = String::from("tic");
    let s2 = String::from("tac");
    let s3 = String::from("toe");

    let s = format!("{}-{}-{}", s1, s2, s3);
}

8.2.4 索引到字符串

rust的String不支持Index. 原因是UTF8编码中,不同字符的长度是不相同的。

8.2.4.1 内部表示(Internal Representation)

A String is a wrapper over a Vec<u8>. Sting是Vec的封装。

8.2.4.2 字节、标量值和字素簇 (Bytes and Scalar Values and Grapheme Cluster)

Another point about UTF-8 is that there are actually three relevant ways to look at strings from Rust’s perspective:

  • bytes
  • scalar values
  • grapheme clusters (the closet thing to what we would call letters)
8.2.4.3 Slicing Strings

slicing strings可能出错

8.2.5 遍历字符串的方法

#![allow(unused_variables)]
fn main() {
    for c in "नमस्ते".chars() { 
        println!("{}", c);
    }

    for b in "नमस्ते".bytes() { // bytes会返回raw bytes. 注意 valid Unicode scalar values may be made up of more than 1 byte.
        println!("{}", b);
    }
}

8.3 哈希表 (Storing Keys with Associated Values in Hash Maps)

Just like vectors, hash maps store their data on the heap.

8.3.1 创建 Hash Map

use std::collections::HashMap;

let mut scores = HashMap::new();

scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);

use the zip method to create a vector of tuples where “Blue” is paired with 10, and so forth.

Then we could use the collect method to turn that vector of tuples into a hash map

fn main() {
    use std::collections::HashMap;

    let teams = vec![String::from("Blue"), String::from("Yellow")];
    let initial_scores = vec![10, 50];

    let mut scores: HashMap<_, _> =
        teams.into_iter().zip(initial_scores.into_iter()).collect();
}

The type annotation HashMap<_, _> is needed here because

it’s possible to collect into many different data structures and Rust doesn’t know which you want unless you specify.

8.3.2 Hash Maps and Ownership

For types that implement the Copy trait, like i32, the values are copied into the hash map.

For owned values like String, the values will be moved and the hash map will be the owner of those values.

    use std::collections::HashMap;

    let field_name = String::from("Favorite color");
    let field_value = String::from("Blue");

    let mut map = HashMap::new();
    map.insert(field_name, field_value);
    // field_name and field_value are invalid at this point, try using them and
    // see what compiler error you get!

We aren’t able to use the variables field_name and field_value after they’ve been moved into the hash map with the call to insert.

8.3.3 Accessing values in a Hash Map

get会返回Option的Some

    use std::collections::HashMap;

    let mut scores = HashMap::new();

    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Yellow"), 50);

    let team_name = String::from("Blue");
    let score = scores.get(&team_name);

可以使用for遍历Map

fn main() {
    use std::collections::HashMap;

    let mut scores = HashMap::new();

    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Yellow"), 50);

    for (key, value) in &scores {
        println!("{}: {}", key, value);
    }
}

8.3.4 更新Hash Map

8.3.4.1 重写值
    use std::collections::HashMap;

    let mut scores = HashMap::new();

    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Blue"), 25);

    println!("{:?}", scores);
8.3.4.2 Only Inserting a Value If the Key Has No Value
fn main() {
    use std::collections::HashMap;

    let mut scores = HashMap::new();
    scores.insert(String::from("Blue"), 10);

    scores.entry(String::from("Yellow")).or_insert(50); // 如果不存在,则插入值
    scores.entry(String::from("Blue")).or_insert(50);

    println!("{:?}", scores);
}
8.3.4.3 Update a Value Based on the Old Value
fn main() {
    use std::collections::HashMap;

    let text = "hello world wonderful world";

    let mut map = HashMap::new();

    for word in text.split_whitespace() {
        // The or_insert method actually returns a mutable reference (&mut V) to the value for this key.  这里指的是value的。
        let count = map.entry(word).or_insert(0); 
        //  Here we store that mutable reference in the count variable
        *count += 1; // *是dereference符号
    }

    println!("{:?}", map);
}

9 Error Handling

10 Generic Types, Traits, and Lifetimes

11 Writing Automated Tests

12 An I/O Project: Building a Command Line Program

13 Functional Language Features: Iterators and Closures []

14 More about Cargo and Crates.io

15 Smart Pointers

16 Fearless Concurrency

17 Object Oriented Programming Features of Rust

18 Patterns and Matching

19 Advanced Features

20 Final Project: Building a Multithreaded Web Server

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值