镜像资源

新建一个文件 ~/.cargo/config


[source.crates-io]
replace-with = 'tuna'
[source.tuna]
registry = "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git"

构建帮助文档

cargo doc --open

语法

:: 语法

表示静态方法调用, 例如: String::new(), 这个静态方法是定义在 structure 中的一个 function, 常常用来生成一个 struct 的实例, 注意 struct 中的 method 和 method 的区别. :: 既可以用于关联 function, 也可以用于 module 的 namespace.

->

声明方法返回值.

=>

match 的匹配项的返回值.

match 和 if

match 必须枚举所有可能的项目, 例如下面的例子是错误的, 因为在 i32 的范围内, 我们只考虑了 1 的情况.

let n : i32 = 5;
match n {
	1 => 5,
}

使用注意: match 的 PATTERN 中的变量如果没有声明 Copy Trait, 则是 move 进去的.

    struct x {
        a: Option<String>
    };

    let v = x {a: Some("1".to_string())};

    let v2 = match v.a {
        Some(s) => s,  // move occur, because s doesn't have Copy trait
        _ => "".to_string(),
    };

    // println!("v.a = {:?}", v.a); // error
    println!("v2 = {}", v2); // 1

// ------------------------------------------
    
    struct x2<'a> {
        a: Option<&'a str>
    };

    let v = x2 {a: Some("1")};

    let v2 = match v.a {
        Some(s) => s, // copy occur, because s is &str
        _ => "",
    };

    println!("v.a = {:?}", v.a); // no error
    println!("v2 = {}", v2); // 1
//------------------------------------------------

    #[derive(Debug)]
    struct x3<'a> {
        a: y3<'a>,
    };

    #[derive(Debug)]
    struct  y3 <'a> {
        b: String,
        c: Option<&'a str>
    }

    let v = x3 {a: y3{b: String::from("1"), c: Some("abc")}};

    let v2 = match v {
        v => "1", // move occur because v doesn't have Copy trait.
        _ => "0",
    };

    // println!("v.a = {:?}", v.a.c); // error
    println!("v2 = {}", v2); // 1
// ------------------------------------------------
    #[derive(Debug)]
    struct  y3 <'a> {
        b: String,
        c: Option<&'a str>
    }

    let v = x3 {a: y3{b: String::from("1"), c: Some("abc")}};

    let v2 = match v {
        val => "1", // move occur because v doesn't have Copy trait.
        _ => "0",
    };

    // println!("v.a = {:?}", v); // error
    println!("v2 = {}", v2); // 1

Match Pattern example:

// 参数中的 String 不能 move (因为没有实现Copy trait), 所以只能引用. 
// x: &mut i32, y: &mut String
fn print_coordinates((x, y): &mut (i32, String)) {
    y.push_str("rooms");    // 可以直接调用方法
    (*y).push_str("rooms"); // 也可以解引用后调用(通过方法修改值)
    *y = "1".to_string();   // 修改参数变量值(直接修改)
    *x = 2;                 // 修改参数变量值(直接修改)
    println!("Current location: ({}, {})", x, y); // 
}

// Listing 18-7: A function with parameters that destructure a tuple
// t 的类型是 mut (i32, String), 参数 tuple move 到了这里.
fn print_coordinates2(mut t: (i32, String)) {
    t.0 = 2;               // 修改值(直接修改)
    t.1 = "1".to_string(); // 修改值(直接修改)
    t.1.push_str("123");   // 修改值(通过方法)
    println!("Current location: ({}, {})", t.0, t.1);
}

// 试图 move 类型 String, 可以
fn print_coordinates3( (x, y):  (i32, String)) {
    
    println!("Current location: ({}, {})", x, y);
}

// // 试图 move 类型 String, 因为 String 没有 Copy 功能, 然而 String 在一个引用中,
// // 所以无法 move.
// fn print_coordinates4( &mut (x, y):  &mut (i32, String)) {
//     // x.dd();
//     println!("Current location: ({}, {})", x, y);
// }

fn print_coordinates5(&(x, y): &(i32, i32)) { // 
    // x.dfd();
    println!("Current location: ({}, {})", x, y);
}

warnning

关闭警告

#[allow(unused_variables)]

#[allow(unused_mut)]

&

引用一个变量, 称为 borrow.

用 * 将一个引用变为对地址的引用.

()

() is a tuple which have nothing. It was also called unit type in other languages.

Annotation

#[allow(unused_variables, unused_mut, unused_assignments)]

#[cfg(test)]

#[test]

#[allow(dead_code)]

dyn

dynamic dispach trait object.

03

03 01 1

注意, 常量永远是 immutable. const 类型必须标明类型.

将所有硬编码变量声明为 const.

shadowing
使用 let 可以将一个 immutable 变量重新赋值.

为什么使用 Immutable?

如果我们认为一个变量不会改变, 基于此写了代码A, 但在未来的某个时间点它被另一段代码B改变了, 就会导致代码A的行为超于预期, 由此产生的Bug是很难处理的. 所以使用了 immutable , 这样一来, 由 rust 保证这个变量是不变的, 您无需跟踪变量是否被改变就能保证代码A的正确性. 另外, immutable也用来改善并发程序.

03-02

无符号比有符号的正整数范围大一倍+1

数字默认是 i32

Number literals Example
Decimal 98_222
Hex 0xff
Octal 0o77
Binary 0b1111_0000
Byte (u8 only) b’A’

通过模式匹配分解 tuple

fn main() {
let tup = (500, 6.4, 1);

let (x, y, z) = tup;

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

}

元组

组合不同类型的元素

数组

数组分配在 stack 上. 是不变的.
用来定义年份等.
let a: [i32; 5] = [1,2,3,4,5];
let a = [3; 5];

定义字节数组:

let arr = b"This is a byte string.";

上面的代码定义了一个字节数组 arr, 其类型是 &[u8; 22].

初始化数组:

let a = [3; 5];

上面的代码初始化了一个长度为 5, 初始值为 3 的数组. 即等同于 let a = [3,3,3,3,3];

vector

可变的.

03 03

方法定义没有顺序
方法参数必须声明其类型.

let x = (let y = 6); 这样的语句是错误的. 因为一个声明块没有返回值.

let x = y = 3; 也是错误的.

表达式是用的最多的, 在 rust 中.

let x = 6; // 这里的 6 是一个表达式.

{} 这也是一个表达式
通常在方法的最后一行返回, 也可以使用 return 提前返回.

03 05

if 必须要{}
if 的{} 可以是代码块, 也可以表达式.

loop 中的 break 可以将值返回.

04 Ownership and Reference

04 01 Ownership2

复杂的数据类型保存在堆上, 有自己的 owner.

下面这些数据类型直接保存在栈上, 且实现了 Copy trait, 在进行赋 let a = 1; let b = a; 这样的赋值时, 会调用 a 的 copy 方法, 相当于 let a = 1; let b = a.copy(); 所以没有发生 move, 也就是说, 没有改变 a 的 owner.

  • All the integer types, such as u32.
  • The Boolean type, bool, with values true and false.
  • All the floating point types, such as f64.
  • The character type, char.
  • Tuples, if they only contain types that also implement Copy. For example, (i32, i32) implements Copy, but (i32, String) does not.

Move 的场合:

  • 赋值操作, 例如 let a = b; 如果 b 没有实现 Copy trait, 则会发生 move
  • 调用函数或者方法, 例如 foo(p: Person), 会发生 move, 但 foo(p:& Person) 或者. foo(p:&mut Person) 则会发生 borrow. 具体看 [[#^79ef45]]
  • Match Pattern 操作

❗❗ 注意无论是全部 move 还是 部分 move, 其源变量不能是引用.

调用方法b, 方法b会将本地变量保存在栈上.

ownership作用

  • 跟踪哪些程序用了哪些变量在堆上
  • 减少堆上的重复数据
  • 清理堆上无用的数据

ownership规则, 3个

  1. Rust 中所有的 value (分配在内存中的值) 都有一个与之相应的变量作为其 owner
  2. 同一时刻只能有一个 owner
  3. 当 owner 结束其作用域时, 变量也被丢弃

注意事项

  • 设计方法的时候, 注意是否需要持有变量的 owner
  • 设计方法的时候, 返回值是否要改变owner
let s = "hello";
let s = String::from("hello");

前者是字面量, 其内存的值不可变, 即使 mut 也不行, 因为它分配在栈上, 它有固定大小.
后者存在堆上, mut 后可变.

一个变量超出 scope 后(离开方法{}之后), rust 调用 drop 函数, 变量的持有者在 drop 函数中释放变量所占有的内存.

复合变量的复制实际上移动了 owner. move 可以从 immutable 变成 mutable.

let s1 = String::from("hello");
let s2 = s1;
// s1 已经无效了.
// 方法调用也是同样的, 例如
let s3 = String:from("hello");
foo(s3);
fn foo(s: String) { ... }
//  println!("s3: {}", s3); // error

上面的问题使用 clone() 可以解决, clone 会在堆上重新分配一个与 s1 具有相同内容的对象. (但不是同一个对象).

let mut a = String::from("hi");
let b = a.clone();
a = String::from("hello");
println!("a {}", a); // a hello
println!("b {}", b); // b hi

调用完 foo 后, s3 变量也无效了. 如果还想使用 s3, 可以在foo() 方法中返回一个元组, include the parameter of input.

fn foo(s: String) {
  let size = s.len(); // 模拟一个业务
  (s, size) // 返回业务结果, 同时返回 s
}

let s4 = "a";
s4 = foo(s4).0;

这里产生了一个问题, 每次调用方法都要返回原始参数值.

How we resolve this problem.

栈和堆

如何分配

栈上的数据必须有确定的大小.

对于那些未知大小的数据或者可能在运行时改变大小的数据, 则必须保存在堆上.

栈上分配更快, 因为无需查找可用空间.

在堆上分配则需要查找到足够的空间, 还要记录每个数据所占用的空间以便进行下一次分配

如何使用

栈的读取更快.

堆的读取则需要跟随指针进行跳转. (指针可以分配在栈上, 因为指针的大小是固定的.)

当调用一个函数的时候, 传递给函数的指针(对变量的引用)以及函数本身的局部变量都会被压入栈, 在函数体结束的时候, 它们都会被弹出栈.

Ownership 要处理的问题就是: 跟踪那些使用了堆空间的代码, 减少堆中重复的数据, 清理堆中无用的数据, 从而避免内存溢出.

理解 Ownership 存在的意义就是为了管理堆, 可以帮助你理解代码工作的方式.

04 02 Reference

Think about reference in Java. Just post reference of object when call a method.

String a = new String

Ref in Rust is a reference pointed the owner of original variable.

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.len()
}

![[Pasted image 20210721232715.png]]

s -> s1 -> data in memory

s is not owner of the object on heap. s1 is owner.

remember that s is immutable, so you can’t change the value in calculate_length() unless use &mut.

Mutable reference

to address data races, Rust don’t allow use &mut more than one time with same variable.

scenario:

  • multiple read was allowed
    let mut s = …
    let r = &s;
    let r1 = &s;

  • mix read and write wasn’t allow
    let mut s = …

    let r1 = &s; // no problem
    let r2 = &s; // no problem
    let r3 = &mut s; // BIG PROBLEM

上述方式解决了同步变量的问题. 可以同时读, 但不能同时写, 即在一个 scope 中, 只能有一个写操作, 离开作用域后, 变量无效, 例如:

对于 i32 等原始的类型, 直接赋值就是 copy, 保存在堆栈上. 使用 & 则是引用, Rust 把 & 表述为 borrow, 临时借用一下, 所以原始值你不能变哦, 看下面的例子

    let mut x = 5;
    let y = x; // copy value of x to y, store on stack
    println!("y = {}", y);
    x = 6; 
    println!("y = {}", y);

    
    let mut x = 5;
    let y = &x; // reference to x, y is borrow of x
    println!("y = {}", y);
    x = 6;  // x is borrowed, and will use in the future, so it won't be modify
    println!("y = {}", y); // here, use y that is borrow of x

编译出错, 因为 x 已经 borrow 出去了, 所以 x = 6 是非法的操作.

Borrow rules: ^79ef45

  • There are only one mutable reference or any number of immutable exists at one time. (but not both of)
  • Reference must be valid.
/*
let mut y = String::from("1");
y 的数据类型(指向的内存区域的类型)是 String, 
y 是资源 String::from("1") 的所有者(owner), 
在编译期, y 被替换为真正保存了 "1" 这个数据的内存地址. 例如: 0x09e3a317
因为使用了 mut, 所以 y 区域的内容可以修改, 
换句话说, 因为使用了 mut, 所以 "1" 所在的内存区域是可以被编辑的.
总结: 申请了一块可以编辑的内存标记为 y, 里面存放了 String::from("1")
*/
let mut y = String::from("1");  // 注意, y 是 owner, 决定了什么时候可以释放内存. 即 0x09e3a317 地址一旦离开作用域, 将被回收.
y = String::from("11");         // 编辑内存中的内容
println!("y: {}", y);   // y: 11
println!();

/*
& 在 Rust 中叫 borrow, 暂时借用, 并不是变量的主人, 所以不拥有变量. 
& 可以理解为 c语言 中的取地址操作, 例如:
&y 的意思是: 借用一下 y. 那怎么借用呢? 我只要 y 的地址, 通过 y 和数据打交道, 所以 y 还是数据的owner, 我只是借用一下,
&mut y 的意思是, 借用一下, 同时还有修改数据的权限
例如:
let a = &y; 的意思是, 申请了一块只读内存, 标记为a, 里面存放的是 y 的地址(且不能修改成别的地址). a 只可以使用但不能修改 y (这个 a 不就是指针嘛)
let a = &mut y; 的意思是, 申请了一块只读内存, 标记为a, 里面存放的是 y 的地址(且不能修改成别的地址). a 有权修改 y 的内容( a 是一个可变指针) 
let mut a = &y; 的意思是, 申请了一块可读写内存, 标记为a, 里面存放的是 y 的地址(也可以修改成存放 z 的地址). a 只可以使用但不能修改 y (这个 a 不就是指针嘛)
let mut a = &mut y; 的意思是, 申请了一块可读写内存, 标记为a, 里面存放的是 y 的地址(也可以修改成存放 z 的地址), a 有权修改 y 的内容( a 是一个可变指针) 

borrow 有两个规则:
1. 同一时刻, 一个资源只能有一个 &mut borrow 或者多个 & borrow, 
2. 引用必须是有效的. 即不能引用一个不存在的东西.

borrow 的两个规则可以可以理解为读写锁, 
	在一个作用域(事务)当中, 对资源进行锁定以避免竞争条件, 要么都是读锁(s), 要么只有一个写锁(x).
	& 理解为读锁.
	&mut 理解为写锁.
这样设计的好处: 避免一段代码中不同的逻辑之间的干扰(类似事务之间的干扰)
隐含的 borrow: 在方法参数中, 例如 println!()
*/
{
	println!("-------------- Block A");
	let a = &mut y;             // a 的数据类型(指向的内存区域的类型)是 &mut String, 通过 mut 引用 y (拥有了 y 的 x 锁), 
	// let mut c = &mut y;      // c 也想要 y 的 x 锁, 这是不可能的, y 上只能加一把 x 锁. 已经被 a 占用了, 且 a 还没有释放.
	// y = String::from("111"); // owner 当然有最大权限, 但此时因为 y 上有一把 x 锁被 a 占用, 且 a 还没有释放, 所以 owner 也无权修改.
	// println!("y: {}", y);    // 这里的 y 作为了方法参数, 是 & borrow (相当于 s 锁), 因为 y 上有 x 锁, 这个 borrow 是不被允许的.
	*a = String::from("2");     // a 拥有 x 锁, 可以写 y. 此后 a 作用域结束, 释放了 x 锁.
	println!("y: {}", y);       // 现在 y 上没有任何锁, y: 2
	y = String::from("111");    // 因为 a 的作用域已经结束, y 上没有锁, 所以现在 owner 可以修改了.
	let mut c = &mut y;         // y 上没有锁, c 现在可以加一把 x 锁
	//println!("y: {}", y);     // y 上有 c 加的锁, 且 c 还没有释放, 所以不能再 borrow
	*c = String::from("3");     // c 修改了 y, 此后 c 作用域结束, 释放 x 锁.
	println!("y: {}", y);       // 现在 y 上没有任何锁, y: 3
	println!();
} 

{
	println!("-------------- Block B");
	let a = & y;                // a 拥有了 y 的 s 锁
	// let c = &mut y;          // c 想要 y 的 x 锁, 这是不可能的, 因为 s 和 x 不能共存, a 已经持有 s 锁且 a 还没有释放, 所以无法加 x 锁.
	// y = String::from("111"); // owner 当然有最大权限, 但此时因为 y 上有一把 s 锁, 且还未释放, 所以 owner 也无权修改.
	let c = & y;                // 但 c 可以得到 y 的 s 锁, 共享锁可以加多次, 不多 c 并没有使用, 所以随即这把 s 锁就失效了
	println!("y: {}", y);       // 这里的 borrow 相当于 s 锁, 因为 s 锁可以加多次, 所以没有问题. y: 3
	println!("a: {}", a);       // 此后 a 作用域结束, 释放了 s 锁. a: 3
	y = String::from("111");    // y 上没有任何锁, y 是owner, 当然可以修改
	println!("y: {}", y);       // y: 111

	println!();
}
let ref mut a = 5;

This is useful in destructuring to take references to multiple inner values.

04 03 slice

A string slice is a reference to part of a String

slice data structure? #todo

&str is string slice type.

Slice is a pointer pointing to a specific point.

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

    let hello = &s[0..5];  // Remeber to use &
    let world = &s[6..11];
}

![[Pasted image 20210704163935.png]]

Concatenate str

format!("{}{}", &str1, &str2);

https://stackoverflow.com/questions/30154541/how-do-i-concatenate-strings

05 Structs

05 01 Using

Using the field init shorthand if when variables and parameters have same name

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

Update syntax

    let user2 = User {
        email: String::from("another@example.com"),
        username: String::from("anotherusername567"),
        active: user1.active,
        sign_in_count: user1.sign_in_count,
    };

声明结构体:

struct MyBag;  // unit-like struct
struct Meter(i32); // tuple struct
struct Coordinate(i32, i32); // tuple struct
struct Person{name: String, age: i32,}  // 花括号后面没有分号 `;`, rust中的花括号不用 `;` 做结尾
  • unit-like struct is used for implement some trait but don’t have any data to store in the type itself.
  • tuple struct is used for naming a tuple, therefore a tuple have a meanful name.

self in implementation of struct means the object itself or current module.

Self is a type of module or struct. &self is a shorthand of self : &Self

self when used as first method argument, is a shorthand for self: Self. There are also &self, which is equivalent to self: &Self, and &mut self, which is equivalent to self: &mut Self.

Self in method arguments is syntactic sugar for the receiving type of the method (i.e. the type whose impl this method is in). This also allows for generic types without too much repetition.

https://stackoverflow.com/questions/32304595/whats-the-difference-between-self-and-self

声明

特殊情况

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

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

06 enumeration

什么是枚举

枚举是一种数据类型(可以想象成一个类), 这个类包含一个有边界的该种类型的数据集, 该数据集不能动态扩展.

例如: 下面的 Person 包含了一个 Person 类型的数据集, 其中有两个元素 MAIL 和 FEMAIL.

enum Person {
	MAIL,
	FEMAIL,
}

可以给类型中添加元素:

enum Person {
	MAIL("Lee"),
	FEMAIL("Lily"); // NOTICE here, use ; instead of , 
	
	String name;
	private Person(String name) {
		this.name = name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public String getName() {
		return this.name;
	}
}

枚举的使用: 当定义的数据类型存在不同的子类型时, 例如: 定义一个返回值 Result 类型, 且这个返回值类型有正常和异常两种情况, 这种场合就是一个典型的枚举.

enum Result<T, E> {
    Ok: T,
    Err: E,
}

07 Package, Crates, and Modules

  • Packages: A Cargo feature that lets you build, test, and share crates. cargo 建立的一个工程是一个 package. 一个 package 包含一个或者多个 crate. 一个 package 只能包含 0 个或者 1 个 Lib crate, 但可以包含多个二进制可执行文件 crate. 且一个 package 必须有一个 crate.
  • Crates: A tree of modules that produces a library or executable. 一个 crate 是一个可执行文件或者一个库文件, crate 相当于一个命名空间. 里面包含 module 的树形结构. main.rs 是一个 crate 的根. lib.rs 也是一个 crate 的根. Rust 默认 main.rs 和 lib.rs 都是 crate, 且名字和 package in Cargo.toml 一样. 一个 package 可以多个二进制 crates, 只要把它们放在 src/bin 下即可.
  • Modules and use: Let you control the organization, scope, and privacy of paths. 提供一个逻辑的分层. 注意: 所有模块都指向根模块 crate. The word “mod” indicate that there is certainly a crate or a fold.
  • Paths: A way of naming an item, such as a struct, function, or module
Package
   crate 1
     module a
       module a-1
       module a-2
   crate 2
     module a
       module a-1

07 01 Packages and Crates

  • Package: cargo.toml 文件所在项目的名字, 定义在 cargo.toml 的 [package].name 中.

  • Binary crate: 可执行 crate

  • Library crate: 库 crate

  • 新建一个 binary: cargo new <package_name>

  • 新建一个 library: cargo new --lib <package_name>

包的绝对引用和相对引用:

  • 绝对引用: 如果 lib 是固定的, client 有可能变化, 用绝对引用
  • 相对引用: 如果 lib 和 client 的相互关系是稳定的, 用相对引用

https://doc.rust-lang.org/book/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html#:~:text=The%20reason%20is%20that%20child%20modules%20wrap%20and%20hide%20their%20implementation%20details%2C%20but%20the%20child%20modules%20can%20see%20the%20context%20in%20which%20they%E2%80%99re%20defined

07 04 use

use 可以用于

  • module
  • struct
  • enum
  • function : 注意很少使用 use 来导入一个 function. 因为使用的时候容易产生混淆.
use std::cmp::Ordering;
use std::io;

equals ->

use std::{cmp::Ordering, io};
use std::io;
use std::io::Write;

equals ->

use std::io::{self, Write};  // use std::io and std::io::Write
use std::collections::*;  // used for test

导入一个模块的场合, 直接在根目录下加一个文件 front_of_house.rs, 然后在 lib.rs 中使用 mod :

mod front_of_house;

如果 front_of_house 中还包含单独文件的子模块, 则那个子模块必须放在 front_of_house 文件夹中.

/-
 |   lib.rs
 |   front_of_house.rs
 |---front_of_house
	   front_of_house.rs

这样的原因是因为 rust 的 module 是 full qulification. 也就是说存在 这样的 module:

  • io.rand
  • app.rand

如果把每个模块单独定义在文件中, 就会出现两个名为 rand.rs 的文件.

crate 可以作为相对路径中的根节点. 注意: 一个 package 中的多个 crate 具有相同的名字, 所以 crate 指的是当前文件所在 crate 的根.

2018 以后不使用 external crate 了.

Re-exporting

  • Export module by different name
  • Export internal module while keep root module invisiable.

syntax:

pub use module::submodule;
pub use module::submodule as other_name;

for example:

lib.rs

mod country {
    pub mod region {
        pub fn hello() {}
    }
}

pub use country::region;            // re-exporting
pub use country::region as area;    // re-exporting

main.rs

use use_modules::region;

fn main() {
    region::hello();
}

use use_modules::area;

fn test() {
    area::hello();
}

use use_modules::country::region; // compile error. country is private.

08

08 01 Vector

Vector 中的元素使用的时候加上 *.

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

迭代 vec 的时候, 会发生move, 因为会调用 vec 的 into_iter(self) 方法.

    struct MyWrap {
        vals: Vec<String>,
    };

    let wrap = MyWrap{vals: vec![]};
    for t in wrap.vals { // 因为会调用 fn into_iter(self), vec 中的值都会被 move 
       
    }

    let wrap = MyWrap{vals: vec![]};
    let ref_wrap = &wrap;
    for t in ref_wrap.vals {
        /*
        ref_wrap 是引用, 不能从引用中 move 出来, 这个场合可以使用 drain(..) 方法.
        cannot move out of `v.vals` which is behind a shared reference
        */
    }

vector to string

let ans:String = String::from_utf8(vector).unwrap();

08 02 String

rust 核心只有一种 string type, 那就是 str. 而 String 类型是标准库函数提供的. 标准库也提供其他类型的 string type, 例如: OsString OsStr CString CStr

String 实际上也是一个指针, 指向 str.

![[Pasted image 20210805151534.png]]

看下面: string 开头的表示 string, string_slice 表示 &str

    string_slice("blue");
    string("red".to_string());
    string(String::from("hi"));
    string("rust is fun!".to_owned());
    string("nice weather".into());
    string(format!("Interpolation {}", "Station"));
    string_slice(&String::from("abc")[0..1]);
    string_slice("  hello there ".trim());
    string("Happy Monday!".to_string().replace("Mon", "Tues"));
    string("mY sHiFt KeY iS sTiCkY".to_lowercase());

Methods on String :

  • push()
  • push_str()
  • split() -> Split
  • trim() -> &str

String to Iterator

s.chars() -> Chars

String to Vec<&str>

let v: Vec<&str> = s.trim().rsplit(’ ').collect();

Reverse string

let ans = ans.chars().rev().collect::()

08 03 HashMap

对于实现了 Copy 的类型, 例如 i32, HashMap 持有副本, 对于 Ownerable 的类型, 例如 String, 会将 owner 交给 HashMap.

更新已有值, 下面的例子是统计单词出现的次数:

    let text = "hello world wonderful world";

    let mut map = HashMap::new();

    for word in text.split_whitespace() {
        let count = map.entry(word).or_insert(0); // return the value if exist, or insert new value and return 0
        *count += 1;    
    }

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

entry() 和 or_insert() 返回的是 &mut v , 通过 * 解引用后这个值可以直接修改.

  • Insert value: map.insert()
  • Get a value: map.get(&key)
  • Get a entry or creat if no exist: map.entry()

09 Error Handling

如果正在写一个函数, 当错误发生时, 不要 panic, 而是把错误返回.

Unrecoverable Error with Panic!

panic!()

fn main() {
    panic!();
}

fn test_error() {
    let vec = [1,2,3];
    vec[99];   // here cause 
  
  
   
   panic
  
  
}

Recoverable Error with Result

Some example:


fn main() {
    let file = File::open("not_exist.txt");
    
    match file {
        Ok(file) => file,
        Err(err) => 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)
            }
        },
    };
}


fn main() {
    let f = File::open("hello.txt").unwrap_or_else(|error| {
        if error.kind() == ErrorKind::NotFound {
            File::create("hello.txt").unwrap_or_else(|error| {
                panic!("Problem creating the file: {:?}", error);
            })
        } else {
            panic!("Problem opening the file: {:?}", error);
        }
    });
}

Propagating Error

Some examples:

use std::fs::File;
use std::io;
use std::io::Read;

fn read_username_from_file() -> Result<String, io::Error> {
    let f = File::open("hello.txt");

    let mut f = match f {
        Ok(file) => file,   
        Err(e) => return Err(e),
    };

    let mut s = String::new();

    match f.read_to_string(&mut s) {
        Ok(_) => Ok(s),
        Err(e) => Err(e),
    }
}


fn read_username_from_file() -> Result<String, io::Error> {
    let mut f = File::open("hello.txt")?;   //  <- here
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}

fn read_username_from_file() -> Result<String, io::Error> {
    let mut s = String::new();

    File::open("hello.txt")?.read_to_string(&mut s)?;  // <- here

    Ok(s)
}



use std::fs;
use std::io;

fn read_username_from_file() -> Result<String, io::Error> {
    fs::read_to_string("hello.txt")
}

10 Generic Types, Traits, and Lifetimes3

trait 两个作用:

  • Trait: 定义行为接口
  • Trait Bound: 约束泛型, 例如 <T: Display>, 指的是实现了 Display trait 的类型

Rust 使用了单态化, 所有的泛型在编译期会被替换成实际的类型. 也就是说, 如果一个泛型方法被调用, Rust 会维持多个实例方法对应着每个调用. 这样就不会因为使用了泛型而牺牲性能. (但是我想, 这样牺牲的是空间, 因为泛型参数的增加可能导致在编译期生成很多实例.)

Generic


let a = Vec::<Pack<String>>::new();
let b = Vec::<String>::new()

traits4

约束条件: trait 和 待绑定的结构必须至少有一项是 local的. 也就是说, 不能将第三方库中的 trait 与第三方库中的结构绑定在一起.

trait 中的默认方法可以调用 trait 当中的其他方法, 即使那些方法抽象的. 这和抽象类有些类似. 如果方法 a() 是一个默认方法, 我们覆盖了 a(), 则不能在我们的方法当中调用默认方法 a().

Trait Bound

限制泛型的范围. 可以理解为: 要求一个参数(任意类型)具有指定的特性.

// single
pub fn notify(item1: &impl Summary, item2: &impl Summary) { // both item1 and item2 must have implemented Summary

pub fn notify<T: Summary>(item1: &T, item2: &T) { // Same as previous notify()

// multiple
pub fn notify(item: &(impl Summary + Display)) { // item must has implemented Summary and Display

pub fn notify<T: Summary + Display>(item: &T) { // Same as previous notify()

// use where clause
fn some_function<T: Display + Clone, U: Clone + Debug>(t: &T, u: &U) -> i32 { // t must has implemented trait of Display and Clone, u must has implemented trait of Clone + Debug.

fn some_function<T, U>(t: &T, u: &U) -> i32
   where T: Display + Clone, U: Clone + Debug { // 
  
  
   
   convenient
  
   implements

return a trait

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 returns_summarizable() -> impl Summary {  // HERE
    Tweet {
        username: String::from("horse_ebooks"),
        content: String::from(
            "of course, as you probably already know, people",
        ),
        reply: false,
        retweet: false,
    }
}

10 03 通过生命周期校验引用

The Borrow Checker5

泛型生命周期语法

&'<lifetime> [mut] <type>

例如:

&i32        // a reference
&'a i32     // a reference with an explicit lifetime
&'a mut i32 // a mutable reference with an explicit lifetime

生命周期本身没有意义, 其意义在于指出各个参数之间的生命周期的关系. 返回值生命周期是 input lifetime 的交集.

Rust 根据三个规则推断 lifetime in function or method declaration

  1. 每一个引用的参数默认有自己的生命周期. 例如: 对于 fn foo(x:&i32, y: &i32) 来说, Rust 默认会为每个参数指定一个生命周期: fn foo<'a, 'b>(x: &'a i32, y: &'b i32).
  2. 如果只有一个输入参数, 则所有的输出参数与输入参数的生命周期保持一致. 例如: 对于 fn foo(x: & i32) -> &i32 来说, Rust 自动指明生命周期 fn foo<'a>(x: &'a i32) -> &'a i32.
  3. 如果输入参数中有 &self 或者 &mut self, 则输出参数的生命周期与保持一致. 这样一来, 对于所有包含了 &self 或者 &mut self 的 method 来说, 生命周期的声明就可以省略了.

'static means a lifetime duration same as whole application. All string literal have this lifetime.

struct 的生命周期

struct ImportantExcerpt<'a> {
    part: &'a str,
}

ImportantExcerpt 和其字段 part 的生命周期一致.

Lifetime elision

11 test6

11 01 Write a test

自定义的 struct 和 enums 需要实现 PartialEq 和 Debug, 前者用来比较, 后者用来输出. 可以使用 #[derive(PartialEq, Debug)] 在自定义的 struct 和 enums 上.

  • assert!()
  • assert_eq!()
  • assert_ne!()
  • #[should_panic(expected = "")]
    #[test]
    fn greeting_contains_name() {
        let result = greeting("Carol");
        assert!(result.contains("Carol"),
                "Greeting did not contain name, value was `{}`",  // format!
                result);
    }

11 02 Controll a test7

指定测试线程数量

指定线程数量可以避免并行测试对共享资源的读写造成的失败.

$ cargo test -- --test-threads=1

输出

$ cargo test -- --show-output

执行部分测试

$ cargo test <str>

只要包含了 str 的测试方法都会被执行. 例如下面执行了包含 bu 的测试方法, 测试报告中同时显示 8 filtered out, 说明有 8 个测试方法没有执行.

$ cargo test gu

running 2 tests
test tests2::guess_panic ... ok
test tests::guess_panic ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 8 filtered out; finished in 0.00s

忽略测试

#[test]
#[ignore]
fn expensive_test() {
    // code that takes an hour to run
}

We can specify cargo test -- --ignored to include ignored test.

11 03 Integration test

  • Make dir tests in root of project.
  • Add rs file in tests
  • Add annotation [test] on every test method.
  • Notice that every rs file is an independent crate.
  • Run particular test using cargo test --test <name>
// Listing 11-13: An integration test of a function in the adder crate
use controlling_how_test;

#[test]
fn it_adds_two() {
    assert_eq!(4, controlling_how_test::add_two(2));
}

Use setup of common

  • Create a common dir in tests
  • Create mod.rs in common
  • Import module of common by using mod common;
// Listing 11-13: An integration test of a function in the adder crate
use controlling_how_test;
mod common;

#[test]
fn it_adds_two() {
    common::setup();
    assert_eq!(4, controlling_how_test::add_two(2));
}

12 Command Line Program

see program.

13 Closure and Iterator

13 01 Closure

函数不能捕获此法作用域, 闭包可以捕获此法作用域.


fn main() {
	let x = 4;
	fn hello() {
		println!("x: {}", 4); // error
	}

}


一个闭包只能推断绑定到一种类型, 如果是两种, 编译出错, 例如:

    let example_closure = |x| x;

    let s = example_closure(String::from("hello"));
    let n = example_closure(5);

13 02 Iterators

调用 ite.next() 需要 mutable, 因为其内部状态会变化, 使用 for 则不会

vec.iter() 返回 immutable, vec.into_iter() 返回 mutable.

Methods on iterator

  • rev()
  • skip_while(|| prdicate) skip elements specified by predicate until meet false
  • take_while(|| prdicate) take elements specified by predicate until meet false

14 Cargo and Crates.io

14 02 Publishing

文档注释 ///, 文档注释中的测试代码可以通过 cargo test 运行.

/// 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
}
$ cargo test

   Doc-tests my_crate

running 1 test
test src/lib.rs - add_one (line 5) ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.27s

模块注释 //!

14 02 Cargo

Run particular package in a workspace.

$ cargo run -p <package>

Test particular package in a workspace.

$ cargo test -p <package>

14 04 Installling binaries

cargo install <binary name>

15 Smart pointers

普通的 reference 只是 borrow 了 data, 而 smart pointer 则可以是 owner.

Smart pointer implements Deref and Drop trait.

15 01 Box8

Box 将数据保存在 heap, 同时在 stack 上保存一个指针, 指向 heap 中的数据.

对于递归的结构

let list = Cons(1, Cons(2, Cons(3, Cons(4, nil))))

![[Pasted image 20210719122643.png]]

Rust 无法知道 list 的实际大小.

改用 Box

let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Cons(4, Box::new(nil))))))))

![[Pasted image 20210719122659.png]]

因为 Box 是一个指针, 有固定的大小, list 其结构不在是递归嵌套, 而是一个链表.

Box 的使用场景:

  • When you have a type whose size can’t be known at compile time and you want to use a value of that type in a context that requires an exact size
  • When you have a large amount of data and you want to transfer ownership but ensure the data won’t be copied when you do so
  • When you want to own a value and you care only that it’s a type that implements a particular trait rather than being of a specific type

Check

15 02 Deref Trait

使用 * (dereference operator) 可以沿着指针得到具体的值. 对于自定义的类型, 如果想直接使用 * , 可以实现 Deref 这个 trait.

struct Mybox<T>(T);

impl<T> Deref for MyBox<T> {
 	type Target = T;
	fn deref(&self) -> &Self::Target {
		...
	}
}

let y = MyBox::new(5);

当我们使用 *y 时, Rust 实际上会这样调用 *(y.deref())

Deref coercion 是自动发生的, 当传入的参数和方法的参数不一致时, Rust 会调用 deref 方法.

Deref coercion 的三种情况(针对不同的 trait):

  • From &T to &U when T: Deref<Target=U>
  • From &mut T to &mut U when T: DerefMut<Target=U>
  • From &mut T to &U when T: Deref<Target=U>

15 03 Drop Trait

Must implement drop() in Drop trait.

Drop trait is used for deallocating memory when variables leave their scope.

All smart pointers had implemented Drop trait.

15 04 Rc<T>

允许多个 owner of immutable 存在.

如果一个值可能被多处引用. 且这些引用的存活时间不一定, 可以使用 Rc<T>.

如果一个变量可能被多处引用, 在声明的时候使用 Rc, 使用的地方调用 Rc::clone(&...) 就行, Rc 的 dereference 是自动的. 所以基本上不需要考虑如何使用 Rc. 另外还有一个查看计数的方法 Rc::strong_count(&...)

对于自定义的结构体, 且有 impl 方法, 如果想用 RefCell 结合 Rc, 这样定义比较好:

enum List {
	Cons(i32, RefCell<Rc<List>>),
	Nil,
}

impl List {
	fn tail(&self) -> Option<&RefCell<Rc<List>>> {
		if let Cons(_, list) = self {
			Some(list)
		} else {
			None
		}
	}
}
enum List {
	Cons(i32, Rc<RefCell<List>>),
	Nil,
}

impl List {
	fn tail(&self) -> Option<&Rc<RefCell<List>>> {
		if let Cons(_, list) = self {
			Some(list)
		} else {
			None
		}
	}
}

if let Some(list) = a.tail() { // tail 方法找不到  method not found in `Rc<RefCell<tests::test99_25::List>>`
	...
}

但是 Rc 用于 Immutable 场合, Mutable 场合怎么办呢?

Rc<T> 只能用于单线程.

15 05 RefCell<T>

Rust 是保守的, 即使你准守了 Borrow 规则, 但 Rust 却无法确定代码的正确性, 因此会拒绝这些代码.

换句话说, RefCell<T> 可以将 borrow 规则的检查延迟到运行时, 这样一来, 你可以实现内部修改这一算法, 即修改那些 immutable 的变量, 但还是要遵守 borrow 规则. [[RUST#^79ef45]]

RefCell<T> 只能用于单线程.

RefCell 有 Rc 的计数功能. 与 Rc 的计数不同, RefCell 通过跟踪对象上的 mut 和 immut 的数量来判断是否满足 borrow 规则 (borrow rules: 同一时刻只允许一个 mut 或者 多个 immut, 不允许 mut 与 immut 同时存在), 计数到0并不意味着释放内存.

Here is a recap of the reasons to choose Box<T>, Rc<T>, or RefCell<T>:

  • Rc<T> enables multiple owners of the same data; Box<T> and RefCell<T> have single owners.
  • Box<T> allows immutable or mutable borrows checked at compile time; Rc<T> allows only immutable borrows checked at compile time; RefCell<T> allows immutable or mutable borrows checked at runtime.
  • Because RefCell<T> allows mutable borrows checked at runtime, you can mutate the value inside the RefCell<T> even when the RefCell<T> is immutable.

15 06 Reference Cycles - Weak<T>


Rc::downgrade(&...)		// get Weak
Rc::weak_count(&...)	// get count
&obj.upgrade() 			// see if have value of Some<T>

16 Concurrency

16 01 Using Threads

use std::thread;
use std::time::Duration;

thread::spawn(|| {
        for i in 1..10 {
            println!("hi number {} from the spawned thread!", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

16 02 Tansfer Data Between Thread

let (tx, rx) = mpsc::channel(); // 

thread::spawn(|| {
	tx.send("hi").unwrap();
});

println!("Got: {}", rx.recv().unwrap());

16 03 Share State

Mutex is a mutual exclusion.

Use Arc instead of Rc in multiple threads.

    use std::thread;
	use std::sync::{Arc, Mutex};
	
    #[test]
    fn test16_13() {
        let counter = Arc::new(Mutex::new(0)); // Arc is atomically reference counted
        let mut handles = vec![];

        for _ in 0..10 {
            let counter = Arc::clone(&counter);
            let t = thread::spawn(move || {
                let mut cc = counter.lock().unwrap();
                *cc += 1;
            });

            handles.push(t);
        }

        for t in handles {
            t.join().unwrap();
        }

        println!("counter = {}", *counter.lock().unwrap());
    }

16 04 Send Trait And Sync Trait

All ownership of values of type implementing Send trait can safty tranfer between threads.

All values of type implementing Sync maker trait can be safty referenced from multiple threads.

All primitive types are Send and Sync, and any type composed entirely of Send are also Send, Sync is the same way.

通常不需要自己实现 Send 和 Sync,

17 OOP

17 01

17 02

与泛型参数不同, trait object 是在运行时确定具体调用方法的, 所以一个 trait object 满足对象安全的条件是:

  • 其方法返回值不能是 Self, 因为 trait object 的类型不固定
  • 不能使用泛型参数

17 03

注意这个用法 request_review(self: Box<Self>) , 这里的的意思有点像 generic bound, 必须在包含了该类型(指的是State)的 Box 上调用才可以.

trait State {
    - fn request_review(self: Box<Self>) -> Box<dyn State>;
    fn approve(self: Box<Self>) -> Box<dyn State>;
    fn content(&self: &Box<Self>, post: &Post) -> &str;
}

struct Draft {} // wait for questing review
struct PendingReview {} // wait for review
struct Published {} // review Ok

impl State for Draft {
    fn request_review(self: Box<Self>)  -> Box<dyn State> {
        Box::new(PendingReview{})
    }
    fn approve(self: Box<Self>)  -> Box<dyn State> {
        self
    }
}

/*
设计一个 List
- List 中可以包含空值 None
- List 中可以包含任何类型
*/

// 用于实现 None
enum Pack {
    Thing(Box<dyn Being>),
    None,
}

// List
struct List {
    values: Vec<Pack>,
}

// 所有 List 中的元素都必须实现这个 Trait
trait Being {
    fn value(&self);
}

// 自定义类型
#[derive(Debug)]
struct Person {}

#[derive(Debug)]
struct Book {}

impl Being for Person {
    fn value(&self) {
        println!("Person = {:?}", self);
    }
}
impl Being for Book {
    fn value(&self) {
        println!("Book = {:?}", self);
    }
}

#[allow(unused_variables, unused_mut, unused_assignments)]
fn main() {

    use Pack::{Thing, None};

    let a_book = Thing(Box::new(Book{}));
    let a_person  = Thing(Box::new(Person{}));

    let mut list = List {values: vec![]};
    list.values.push(a_book);
    list.values.push(a_person);

    for thing in list.values {
        if let Thing(t) = thing {
            t.value();
        }
    }
    
}

18

18 02 Refutable and Irrefutable

Irrefutable

let x = 5;

上面的语句永远成立, 这是 irrefutable

refutable

let Some(x) = a_value;

上面的语句也可能不成立, 这是 refutable

18 03 All Pattern

[[变量是什么]]

字面量

    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), // 这里的 y 不是外面的 y
        _ => println!("Default case, x = {:?}", x),
    }

    println!("at the end: x = {:?}, y = {:?}", x, y);

多模式

fn main() {
    let x = 1;

    match x {
        1 | 2 => println!("one or two"),  //  1 或者 2
        3 => println!("three"),
        _ => println!("anything"),
    }
}

范围

    let x = 5;

    match x {
        1..=5 => println!("one through five"),  // equals to 1 | 2 | 3 | 4 | 5 => ... 
        _ => println!("something else"),
    }
}
   let x = 'c';

    match x {
        'a'..='j' => println!("early ASCII letter"),
        'k'..='z' => println!("late ASCII letter"),
        _ => println!("something else"),
    }

解构

Use to destructure structs, enums, tuples, and references

Destructuring Structures

两种解构方式

    struct Point {
        x: i32,
        y: i32,
    }
    
    let p = Point { x: 0, y: 7 };

    let Point { x: a, y: b } = p; // 定义 x 和 y 变量.
    assert_eq!(0, a);
    assert_eq!(7, b);

    let Point { x, y } = p;  // 使用结构体中的变量
    assert_eq!(0, x);
    assert_eq!(7, y);

Match 中解构

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

Destructuring Enums

General

enum Message {
    Quit,
    Move { x: i32, y: i32 }, // anonymous structure
    Write(String),
    ChangeColor(i32, i32, i32), // tuple
}

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

Nested enums

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
        ),
        _ => (),
    }
}

Destructuring Structs and Tuples

let ((feet, inches), Point { x, y }) = ((3, 10), Point { x: 3, y: -10 });

忽略

_

相当于占位符, 不会触发 ownership.

代替整个变量

fn foo(_: i32, y: i32) {
    println!("This code only uses the y parameter: {}", y);
}


代替变量中的一部分

    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);s    
    
    let numbers = (2, 4, 8, 16, 32);

    match numbers {
        (first, _, third, _, fifth) => {
            println!("Some numbers: {}, {}, {}", first, third, fifth)
        }
    }

_开头的变量

Rust 会忽略未使用的下划线开头的变量. 但在 match pattern 中, 仍会触发 ownership .

… 会尽可能多的匹配内容.

fn main() {
    let numbers = (2, 4, 8, 16, 32);

    match numbers {
        (first, .., last) => {
            println!("Some numbers: {}, {}", first, last);
        }
    }
}

Extra Conditionals with Match Guards

19 Advanced Features

19 01 Unsafe

#todo

19 02 Advanced Traits

pub trait Iterator {
    type Item; // Placeholder type is associated type.

    fn next(&mut self) -> Option<Self::Item>;
}

为什么使用 associated type 而不是 generics. 因为泛型允许多个实现.

use std::cell::RefCell;
struct Counter{
    count: RefCell<i32>,
}

trait Ite<T> {
    fn next(& self) -> T;
}

impl Ite<i32> for Counter {
    fn next(& self) -> i32 {
        match self.count.borrow_mut() {
            mut x if *x < 5 => {
                *x += 1;
                *x
            },
            _ => 999
        }
    }
}

impl Ite<String> for Counter {
    fn next(& self) -> String {
        match self.count.borrow_mut() {
            mut x if *x < 5 => {
                *x += 1;
                x.to_string()
            },
            _ => "".to_string()
        }
    }
}

fn main() {
    let count = Counter{count: RefCell::new(0)};
    println!("{:?}", Ite::<i32>::next(&count));
    println!("{:?}", Ite::<String>::next(&count));
    
}

默认泛型类型

  • 在已有的 trait 上增加默认泛型, 这与已经实现的代码没有冲突.
  • 通过默认泛型, 可以扩展现有的功能, 例如: int + int 是默认的功能, 我们可以扩展一个 int + string 的功能
trait Add<Rhs=Self> { // <Rhs=Self>
    type Output;

    fn add(self, rhs: Rhs) -> Self::Output;
}

可以用来改写操作符, 例如 +

使用全限定语法消除调用方法时的歧义

全限定调用:

<Type as Trait>::function(receiver_if_method, next_arg, …);

例如:

  • Trait 方法调用: Trait::Generic::Method(&variable), 例如: Generic::<bool>::some(&one_num))
  • Trait 函数调用: <Type as Trait>::Function(receiver_if_method, next_arg, …), 例如 <Dog as Animal>::baby_name()

根据情况, 如果 rust 能推断出来, 则可以省略其中的内容.

例如: 泛型Trait 的方法调用

trait Generic<T> {
    fn some(&self) -> T;
}

impl<T> Generic<T> for i32
where
    T: Default,
{
    fn some(&self) -> T {
        T::default()
    }
}

fn main() {
    let int: i32 = 45;

    //1
    println!("some: {}", Generic::<bool>::some(&int));
    //2
    println!("some: {}", &int.some() as &bool);
    //3
    println!("some: {}", (|| -> bool { int.some() })());
    //4
    let arg: bool = int.some();
    println!("some: {}", arg);
    //5
    (|arg: bool| println!("some: {}", arg))(int.some())
}

trait 中使用其他 trait

例如: 下面的例子中, OutlinePrint 使用了 Display, 这意味着, 实现了 OutlinePrint 的对象也必须实现 Display.

use std::fmt;

trait OutlinePrint: fmt::Display {
    fn outline_print(&self) {
        let output = self.to_string();
        let len = output.len();
        println!("{}", "*".repeat(len + 4));
        println!("*{}*", " ".repeat(len + 2));
        println!("* {} *", output);
        println!("*{}*", " ".repeat(len + 2));
        println!("{}", "*".repeat(len + 4));
    }
}

使用 Newtype 模式为已有的类型增加 trait

We’re allowed to implement a trait as long as either the trait or the type is local to our carte. That mean that we can’t implement trait of system on the type of system. But it’s possible to get around this restriction using newtype pattern.

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

19 03 Advanced Types

19 04 Advanced Functions and Cloures

Extern Qulifier

Allows function can be called with a particular ABI. Code below show a function named “window_proc” that can be called with ABI “system”.

// Window procedure function to handle events
pub unsafe extern "system" fn window_proc(
    hwnd: HWND,
    msg: UINT,
    wparam: WPARAM,
    lparam: LPARAM,
) -> LRESULT {
    match msg {
        WM_CLOSE => {
            DestroyWindow(hwnd);
        }
        WM_DESTROY => {
            PostQuitMessage(0);
        }
        WM_LBUTTONDOWN => {
            on_lbuttondown(hwnd);
        }
        _ => return DefWindowProcW(hwnd, msg, wparam, lparam),
    }
    return 0;
}

https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier

19 05 Macros

声明式

Declaritive macros look like a function, you can call it by adding ! after macro name.

在宏定义中, 通过模式匹配, 匹配宏参数(即用户代码), 并生成相应的新代码, 用这些新代码替换用户代码. #[macro_export] 相当于 pub , 使得引入了该 crate 的代码可以访问 macro.

#[macro_export]
macro_rules! vec {
    ( $( $x:expr ),* ) => {
        {
            let mut temp_vec = Vec::new();
            $(
                temp_vec.push($x);
            )*
            temp_vec
        }
    };
}
  • $( $x:expr ) : $ 跟上一对括号, 用来表示一个模式. $($x:expr) 意思是匹配任意表达式, 并命名为$x
  • ,* : , 是一个普通的字面量, * 表示 0 次或者多次.

使用声明式的宏时, 只需要在宏名称后面加上一个 ! 号就可以了.

vec![1, 2, 3, 4]    // 宏名称后面加一个 ! 号.

声明式的宏可以像 vec! 这样, 也可以像 println!() 这样使用, 用法多样, 你也可以定义像

过程式

过程式宏的定义过程更像是一个函数, 接收输入参数(即用户代码), 编辑修改这些参数, 然后输出新代码.

三种类型:

  • 自定义 #derive
  • 参数式
  • 函数式

自定义 #derive

例如为类型添加 trait 的声明, 下面定义了一个可以 derive 的宏.

extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn;

#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
    // Construct a representation of Rust code as a syntax tree
    // that we can manipulate
    let ast = syn::parse(input).unwrap();

    // Build the trait implementation
    impl_hello_macro(&ast)
}

fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream {
    let name = &ast.ident;
    let gen = quote! {
        impl HelloMacro for #name {
            fn hello_macro() {
                println!("Hello, Macro! My name is {}!", stringify!(#name));
            }
        }
    };
    gen.into()
}

使用:

use hello_macro::HelloMacro;
use hello_macro_derive::HelloMacro;

#[derive(HelloMacro)]
struct Pancakes;

fn main() {
    Pancakes::hello_macro();
}

参数式

使用宏:

#[route(GET, "/")]
fn index() {

定义宏:

#[proc_macro_attribute]
pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {

函数式

使用宏:

let sql = sql!(SELECT * FROM posts WHERE id=1);

定义宏:

#[proc_macro]
pub fn sql(input: TokenStream) -> TokenStream {

Cargo

cargo run

cargo build # build a exe file

注意事项

  • RUST 中的变量分两类. 可以为 none 的和 不可以为 none 的. 如果一个变量可以为 none, 那么定义的时候一定要定义成 Option. 使用的时候通过 as_mut() 进行引用. 可以将一个 Option 类型的变量看做是一个盒子, 里面可能包含某种类型的变量值, 也可能包含 None 值, 所以在使用之前需要进行处理.
    • as_mut() 改动 Option 中的变量值之前先取得变量值的可变引用
    • unwrap() 取得 Option 中的变量值(可能panic), 如果之前没有 as_mut(), 则发生 move 操作
    • take() 取得 Option 中的变量值, 并用 None 填充原 Option

  1. https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html
    [Application Binary Interface (ABI)]: https://doc.rust-lang.org/reference/abi.html#application-binary-interface-abi
    [The Borrow Checker]: https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#the-borrow-checker ↩︎

  2. https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html ↩︎

  3. https://doc.rust-lang.org/book/ch10-00-generics.html ↩︎

  4. https://doc.rust-lang.org/book/ch10-02-traits.html ↩︎

  5. [The Borrow Checker] ↩︎

  6. https://doc.rust-lang.org/book/ch11-01-writing-tests.html ↩︎

  7. https://doc.rust-lang.org/book/ch11-02-running-tests.html ↩︎

  8. https://doc.rust-lang.org/book/ch15-01-box.html ↩︎

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐