准备工作

rustup

rustup github

rustup 用于安装 Rust , 并可管理 rust 不同的版本(stable, beta, nightly) 并更新它们.

rust 被安装在 $HOME/.cargo/bin 目录下.

cargo install 安装的程序和插件, 同样也在上面的目录中.

安装

curl https://sh.rustup.rs -sSf | sh

在当前窗口加载环境配置
source $HOME/.cargo/env

本地文档

rustup doc

删除

rustup self uninstall

自动补全

# bash
rustup completions bash > $(brew --prefix)/etc/bash_completion.d/rustup.bash-completion
# zsh
rustup completions zsh > ~/.zfunc/_rustup
在 ~/.zshrc 文件中, 在 compinit 之前添加以下语句
fpath+=~/.zfunc

更新

rustup update

rustup self update

安装其他版本

rustup 安装 rust 时, 默认是 stable 版本

 # 安装 nightly 版本
 rustup toolchain install nightly
 # 运行 nightly 版本编译器
 rustup run nightly rustc --version
 # 切换默认版本为 nightly
 rustup default nightly

替换 crate 源

vim  $HOME/.cargo/config
添加以下内容

[source.crates-io]
registry = "https://github.com/rust-lang/crates.io-index"
replace-with = 'ustc'
[source.ustc]
registry = "git://mirrors.ustc.edu.cn/crates.io-index"

cargo 常用命令

cargo new project-name
cargo run
cargo build  --release

# 升级依赖. 成功后, 会更新 Cargo.lock 文件
cargo update

# 为你的项目及依赖生成文档
cargo doc --open

Cargo.lock 文件是你第一次执行 cargo build 后输出的文件.

你的项目在其他人里编译时, 如果 cargo 发现有 Cargo.lock 文件, 则以该文件里的一切配置及版本为准来编译项目. 这方便重现你的项目.

Rust 概念

  • 默认情况下, 变量是不可变的. immutable by default

    • 要想使变量可变, 则要用 mut 来修饰.
  • 默认情况下, 引用也是不可变的. references are immutable by default

    • 要想使引用可变, 则要 &mut 来修饰
  • immutable 表示一旦绑定 bind 就不可以再改变其值.

  • Result 是枚举类型. OkErr .

  • cargo 文件 Cargo.toml 的版本写法 rand = "0.3.14" 等同于 rand = "^0.3.14" 表示任意兼容 0.3.14 版本 API 的版本即可.

  • crate 表示库

  • trait 表示接口

  • shadow : 通常用于同一个变量名, 从一种类型切换到另一种类型.

    • let x = 5; let x = x + 1;
  • shadowmut 的区别

    • 如果没有 let 然后重新声明同样名的变量会编译错误. 通过 let 我们可以方便地从一个变量转变为 immuable
    • let 可以重用原来的变量名, 但类型可以不同. 而 mut 不可以.
  • 指定变量类型 let var_name: var_type

  • _ : 特殊变量, 表示所有.

  • constimmutable 的区别

    • const 不允许用 mut 修饰. 它是一直都不可变的.
    • const 中, 类型必须显式指定
    • const 可以在任何地方声明
    • const 只允许 const 表达式赋值, 而不是一个函数调用或其他在 runtime 时计算出来的值.
  • 数据类型

    • scalar : 表示一个单独的值. 有 4 种.

    • integer

    | length | signed | unsigned | | —— | ————————— | ——– | | 8bit | i8 | u8 | | 16bit | i16 | u16 | | 32bit | i32 (默认,即使是 64 位平台) | u32 | | 64bit | i64 | u64 | | 128bit | i128 | u128 | | arch | isize | usize |

    字面量

    | 类型 | 示例 | | —————— | ———- | | 十进制 | 98_222 | | 十六进制 | 0xff | | 八进制 | 0o77 | | 二进制 | 0b1111_000 | | 字节(仅限 u8 类型) | b’A’ |

    溢出处理

    Debug 模式: 会检测是否溢出. 然后产生 panic 退出.

    Release 模式: 并不会检测溢出. 超出值范围的话, 会产生 two's complement wrapping .

    • foating-point number

    | bit | type | | —- | ———- | | 32 | f32 | | 64 | f64 (默认) |

    • bool

      • 只有两个值 false, true
      • 大小为 1 byte
    • character

      • 使用单引用. 双引用的话是 String 类型.
      • 它是 14 bytes` 大小.
    • compound : 这可以组成多个值为一个类型. 有两种

    • tule

      • 可以由多种类型组合为一种.
      • 它是固定长度. 一旦声明, 则不能改变大小.
      • 声明: let tup =(32, 6.4, 1) . ` 要获取值
      • let (x, y, z) = tup; : 这种称为解构 destructuring
      • 也可以通过下标直接访问 tup.0, tup.1 . 第一个索引的下标为 0.
      • 元素不必是相同类型
    • array

      • 固定长度.
      • 声明 let a = [1,2,3,4,5];
      • let a : [i32; 5] = [1,2,3,4,5];
      • let a = [3; 5] . 表示元素全是 3, 一共 5 个.
  • 函数

    • 风格 fn hello_world()
    • 参数 fn hello_world(x:i32) 参数中的类型是必填的
    • 函数体. 包含许多 statement 以及结尾可选的 expression.
    • statement : 执行一些动作, 但没有返回值.
      • 比如函数声明
      • let
    • expression : 它会给出一个值.
      • 调用函数
      • 调用 macro
      • {} 定义作用域: let y = {let x = 3; x + 1}
    • 注意 5+1 是 expression. 但 5+1; 是 statement.
    • expression 可以是 statement 的一部分.
    • 注意, Rust 是一门 expression-based 的语言. 这跟其他的有很大的不同.
    • 返回值
    • fn hello_world() -> i32 {}
    • 函数体最后一个 expression 就是函数的返回值.
    • 多值(通过 tuple). fn hello_world() -> (i32, i32){}
  • 注释: // your comment

  • 控制流程

    • if expression 注意, 它是表达式来的.
    • if bool-expression {} else if bool-expression2 {} else {}
    • 用在 let 中 : let number = if condition {} else {};
      • 这时, if 与 else 的 expression 结果, 都需要一致.
    • 循环
    • loop
      • 无限循环. loop {}
    • while
      • while condition {}
    • for
      • 遍历数组 for e in arr.iter() {}
      • for number in (1..4).rev() {}
    • 中断循环 : break; 它是 expression . break x * 2; 表示中断, 并返回值 x*2
    • 继续循环 : continue;

Ownership

rules

  • 在 Rust 中的每个值都有一个称为 owner 的变量
  • 同一时间, 只允许一个 owner
  • owner 离开了作用域, 则这个 value 将被 drop 掉.
    • 即 Rust 在变量离开作用域时, 自动调用 drop 函数来释放资源.

stack and heap

  • Stack : 所有保存在 stack 的数据必须是编译期已知的, 固定大小的.
  • Heap : 编译期未知大小的或大小允许被改变的数据则存储在 heap.

变量与数据交互

move

类似其他类型的 shallow copy , 在 Rust 中称为 move .

当一个变量 move 到另一个变量时, 则旧的变量将变成无效了. 即 ownership move 了.

clone

类似其他类型的 deep copy , 在 Rust 中是调用 clone() 方法. 开销比较大, 容易影响性能.

stack only

在 stack 上的数据, 都是 copy . (即不会 move)

在 stack 中的数据, shadow copydeep copy 是一样的.

Rust 不允许在某个类型拥有 Drop trait 的添加 Copy trait .

copy trait 的数据类型

  • integer
  • bool
  • float point number
  • character
  • tuple 并且仅包含是 copy trait 的元素. 例如 (i32, i32) . 但 (i32, String) 则不是.

ownership 与 function

从语义上说, 传递一个值给函数, 跟赋值给一个变量是同义的. 所以,传递一个变量给一个函数, 将会导致 copymove , 像赋值那样子.

返回值也同样会 transer ownership .

references and borrowing

可以通过 reference 一个对象作为参数, 而不是获取它的 ownership. (这称为 borrowing , 即函数参数是 reference , & )

注意, & 即 reference , 默认情况下也是 immutable 的.

要想 & 即 reference 变成 muutable, 则要 let mut s = String.from("hello"); 然后传递reference 时使用 &mut s 即可(相应的函数参数也要指定为 &mut String

mutable 的 reference 有一个很大的限制, 即在同一个作用域内, 只允许一个 mutable reference . 比如下面这样子就不允许:

> let mut s = String.from("hello");
> let r1 = &mut s;
> let r2 = &mut s;
> ```
>
> 也不允许 mutable reference 与 immutable reference 共存.
>
> 但可以允许多个 immutable reference 共存

rust fn get_length(s:&String) -> usize { s.len() }


下面这种则会获取 ownership . 这时  s 在调用完函数后会被 drop 掉.

rust fn get_length(s:String) -> usize { s.len() }


### dangling reference

rust fn main() { let s = no_dangle(); println!(“{}”, s); } // 这个方法是 borrow, ownership 在 dangle() 方法内, 返回后, Rust 会释放 s 的内存. 导致 dangle reference, 所以在编译期就禁止了. fn dangle() -> &String { let s = String::from(“hello”); &s }

// 这个方法是 move, 即将 ownership 移出去, 这样子, Rust 就不会释放 s 的内存了 fn no_dangle() -> String { let s = String::from(“hello”); s }


### reference rule

- 在给定的任意时间, 要么只有一个 mutable reference 要么多个 immutable reference . 但不能共存.
- reference 必须总是有效的.

### slice type

> slice 并不拥有 ownership



rust let s = String::from(“hello”); let slice = &s[2..4];


返回 String slice

rust fn hello_world(s : &String) -> &str { &s[..] }


string 字面值

> 所有 string 字面值, 都是 slice 类型. 即 `&str` .  也意味着它是 immutable 的. 因为 reference 默认是 immutable 的.

其他类型的 slice

rust let a = [1,2,3,4,5]; let slice = &a[1..3]; // slice 是 &[i32] 类型


# struct

## 定义

rust struct User { name : String, email : String, active : bool, }


## 实例化

rust let user1 = User { name : String::from(“yang”), email : String::from(“xx@qq.com”), active : true, };

// mutable let mut user1 = User { name : String::from(“yang”), email : String::from(“xx@qq.com”), active : true, };


> 注意, 整个实例要么是 immutable, 要么是 mutable, Rust 不允许 struct 中部分字段是 mutable, 部分是 immutable

使用函数来初始化可以简化

rust fn build_user(email : String, name : String) -> User { User { email, name, active: true, } }


> 即函数参数跟成员同名即可.

使用旧的 struct 实例来初始化新的

rust let user1 = User { name : String::from(“yang”), email : String::from(“xx@qq.com”), active : true, };

let user2 = User { name : String::from(“yang”), email : String::from(“xx@qq.com”), ..user1 };


> 这表示, user2 中除了 name, email 不同值之外, 其他成员字段值跟 user1一致.

## tuple struct

> 类似 tuple, 但没有名字, 只有类型. 可以像 tuple 那样解构以及通过索引访问

rust struct Color(i32, i32, i32); struct Point(i32, i32);


## unit like struct

它没有任何成员. `()`

通常用于实现一个 trait, 但没有任何数据要存储到这个类型上.

## print

默认情况下, 自定义的 struct 类型并没有实现 `std::fmt::Display` 这个 trait . 所以当使用 `println!()` 来打印 struct 对象时会编译报错. 

可修改为 debug print

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

fn main() { let are = Are { width: 16, height: 16, }; println!(“are -> {:?}“, are); }


> 即 println! 中的 `{:?}` 表示使用 `#[derive(Debug)]`  的 trait 来显示.

## method

> method 不完全同于 function. Method 是定义在 struct (或 enum, 或 trait 对象) 的上下文中的.
>
> Method 可以 move ownership (self), 或 borrow immutable (&self) 或 borrow mutable  (&mut self)
>
> Rust 在调用 method 的时候, 会自动将 struct 转换为 method 签名的类型.(如果兼容的话)

定义 method

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

// struct 允许有多个 impl . impl Are { fn area(&self) -> u32 { self.height * self.width } }

fn main() { let are = Are { width: 16, height: 16, }; println!(“are -> {:?}“, are.area()); }


Method 的第一个参数, 总是 `self` (可以为 `&self` 或 `&mut self`). 

## associated function

在 impl 语句块中的函数第一个参数不是 `self` 的函数, 即是  associated function. 它不是 method, 因为它不与实例关联. 

> String::from 就是一个 associcated function

# enum 与 pattern matching

定义

rust enum IP { V4, V6, } 或 enum Message { Quit, Move {x: u32, y: u32}, Write(String), Color(i32, i32, i32), }


类似 struct, 也可以为 enum 添加 impl . 

> Option enum. 
>
> Rust 中没有 null 这种特性.

match `expression` . 

rust fn value_in_message(message: Message) -> u8 { match message { Message::Quit => 1, Message::Move => 2, Message::Write => 3, Message::Color => 4, } }

//绑定值 fn main() { println!(“{:?}“, plus_one(Some(32))); }

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

// 结合 if let. 它与 match 类似, 当 match 条件匹配时, 则执行某动作. 注意, if let xx 后面是 = 号, 而不是 == // 可以想象为 if let 是 match 语法糖, 它只执行 match 其中之一, 而忽略剩余其他的全部. fn main() { let som = plus_one(Some(32)); if let Some(33) = som { println!(“33”); } }

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


> match 必须匹配所有情况. 特殊的 `_` 表示剩余所有情况.
>
> 因为 match 要匹配所有情况. 所以, 如果你只想处理其中之一的情况, 请记得用 if let 语法.



# Project manage

- Package
- Crates
- Modules 以及 use 
- Paths 

## package and crates

一个 crate 是一个 binary (`main.rs`) 或 lib. (`lib.rs`)

bash

binary

cargo new project

lib

cargo new –lib libname




一个 package 是一个或多个 crate. 一个 package 包含一个 `Cargo.toml` 文件描述如何构建这些 crate.  一个 package 可以包含 0 或至多一个 lib crate.

## module

bash

创建库

cargo new –lib mathlib

添加模块. 修改 src/lib.rs 文件

mod hello { fn say_world(s: &str) { println!(“hello {}”, s); }

mod sub {
    fn say_world(s: &str) {
        println!("hello in sub mod {}", s);
    }
}

}


module tree 

bash 根据上面的情况, module tree 为 crate -> hello -> say_world -> sub -> say_world


## module 引用路径

- 绝对路径: 通过 crate 名字或以 `crate` 开头.
- 相对路径: 在当前模块中, 使用 `self` 或 `super` 或直接当前模块的标识符

rust pub mod hello { pub fn say_world(s: &str) { println!(“hello {}”, s); }

mod sub {
    fn say_world(s: &str) {
        println!("hello in sub mod {}", s);
    }
}

}

pub fn say_world(s: &str) { hello::say_world(s); }


> 默认情况下, rust 所有东西(function, method, struct, enum, module, constant)都是 private 的.  要想导出路径, 则使用 pub 关键字. 

调用父模块的方法(每一次 super , 相当于目录的 ` ../`, 即上级目录)

rust pub mod hello { pub fn say_world(s: &str) { println!(“hello {}”, s); }

mod sub {
    fn say_world(s: &str) {
        println!("hello in sub mod {}", s);
        super::super::say_world_parent(s);
    }
}

}

pub fn say_world_parent(s: &str) { hello::say_world(s); }


## struct 与 enum 的 pub

注意, 在 struct 之前使用 `pub` 表示该 struct 是 pub 的, 但里面的字段仍然是 `private` 的. 但 enum 的话 如果 pub 了, 则所有都默认都是 pub 的.

## use 与 as

use : 导入路径.

rust pub mod hello { pub fn say_world(s: &str) { println!(“hello {}”, s); }

mod sub {
    fn say_world(s: &str) {
        println!("hello in sub mod {}", s);
        super::super::say_world_parent(s);
    }
}

}

use hello::say_world;

pub fn say_world_parent(s: &str) { say_world(s); }


其他用法

rust use std::{com::Ordering, io}; use std::io::{self, Write}; use std::io::*;




as : 别名

rust pub mod hello { pub fn say_world(s: &str) { println!(“hello {}”, s); }

mod sub {
    fn say_world(s: &str) {
        println!("hello in sub mod {}", s);
        super::super::say_world_parent(s);
    }
}

}

use hello::say_world as h;

pub fn say_world_parent(s: &str) { h(s); }


## 将 lib 分离不同文件

bash src/lib.rs src/hello.rs

只要在 lib.rs 文件中使用 mod hello; 即可导入到 lib.rs 合并了.


## 使用本地库

`Cargo.toml`  文件修改:

bash [dependencies] rand = “0.3.14”

注意 这个命名, 要跟库项目中的 Cargo.toml 中的 name 一致.

mathlib = {path = “../mathlib”} # 这里指向库的根目录


使用

rust use mathlib;

fn main() { mathlib::hello::say_world(“fuck”); }


# 常用集合 Collections

> 这些数据结构是保存在 heap 的.

## vector

> 每个元素只能是同一种类型. 不过, 可以利用 enum 来持有其他类型.

rust // 创建 fn main() { let v: Vec = Vec::new(); let vv = vec![1, 2, 3]; }

// 添加/删除元素 fn main() { let mut v: Vec = Vec::new(); v.push(13); println!(“{:?}“, v); v.remove(0); println!(“{:?}“, v); }

//遍历 fn main() { let v = vec![1, 2, 3, 4]; for i in &v { println!(“{}”, i); } }


> 注意. 这里是用 `&v` , 如果为 `v` , 则表示将 let v 的 ownership move 到 for 语句块了. 当结束时, v 就无效了, 即后面的代码不能再访问 v 了. 

## String

> 它是 UTF8 encode 的.

rust let mut s = String::new();

//修改 s.push_str(“hello world”);

//拼接 concat fn main() { let s1 = String::from(“hello “); let s2 = String::from(“world”); let s3 = s1 + &s2; println!(“{}”, s3); } 实际调用的方法签名伪代码为 fn add(self, s:&str) -> String {}

所以, 第一个参数(即 s1)的 ownership 已经 move 了. 之后的代码中, s1 不再有效. 由于 s2 只是借用而非 move, 所以 s2 仍然是 valid 的.

//拼接 2 fn main() { let s1 = String::from(“hello “); let s2 = String::from(“world”); let s3 = format!(“{}{}”, s1, s2); println!(“s1 {}, s2 {}, s3 {}”, s1, s2, s3); }

//索引 String. Rust 的 String 不同于其他的编程语言可以直接根据下标来索引字符. // Rust 的 String 内部是使用 Vec . 即 Vec 来保存的. 即字节 Vec // String.len() 返回的是字节的长度, 而不是字符. // Rust 中的 String 有三种表示. bytes, scalar values, grapheme clusters. // Rust 不允许使用索引来引用 String 的另一个原因是, 索引操作预期是 O(1) 的, 但Rust 中的 String 并不能这样子, 只能从头到尾来决定字符是否有效. // 允许使用索引的情况是返回值为 a byte, a character, a grapheme cluster 或 string slice. “hello”.chars() “hello”.bytes() grapheme 比较复杂, 并没有在标准库中提供.


## HashMap

> 类似 vector, Hashmap 保存在 heap. k 要同一类型. v 也要同一类型.

rust use std::collections::HashMap;

fn main() { let mut scores = HashMap::new(); scores.insert(String::from(“hello”), 10); scores.insert(String::from(“world”), 20);

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

}

//通过两个 vec 来构建 HashMap use std::collections::HashMap;

fn main() { let teams = vec![String::from(“hello”), String::from(“world”)]; let scores = vec![10, 20];

let m: HashMap<_, _> = teams.iter().zip(scores.iter()).collect();

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

}

//遍历 for (k, v) in &m {

}


### ownership

对于实现了 Copy trait 的, 例如 `i32` , 则值将 copy 到 HashMap, 而对于 owned value 的, 例如 `String`, 则 ownership 将会 move 到 HashMap. 例如

rust use std::collections::HashMap;

fn main() { let h = String::from(“hello”); let w = String::from(“world”);

let mut map = HashMap::new();
map.insert(h, w);

println!("{:?}", map);
//println!("h {}, w {}", h, w); h, w 的 ownership 已经 move 到 map 了, 所以 insert 之后, 这两个变量已经 invalid 了. 

}


> 不过, 如果我们插入 reference 的话, 则 ownership 不会 move .
>
> 但这种情况下,  reference 的生命周期至少要与 hashMap 一样有效.

### 更新 hashmap

rust //直接覆盖 let h = String::from(“hello”); let w = String::from(“world”);

let mut map = HashMap::new();
map.insert(h, w);
map.insert(String::from("hello"), String::from("new world"));

//只在 key 没有值的时候插入 map.entry(String::from(“newKey”)).or_insert(String::from(“fuck”));

//基于旧值更新. or_insert 实际返回的是 &mut v, 这样子就可以修改这个值即可. let v = map.entry(xx).or_insert(yy); *v += 1;


### hash function

默认情况下 HashMap 使用的是 siphash 函数. 但它不是性能最快的, 但权衡了安全性. 如果你发现它不能满足的你性能要求, 可以指定其他的 hash function . 

# 错误处理

## 不可恢复的错误 panic

rust panic!(“crash message…”);


当使用该宏时, 会打印 panic 信息, 然后 unwind 以及程序清理栈, 最后退出.

> Unwinding 要执行许多工作. 一个可选的方式是立即 abort . 这会导致程序直接退出, 而不进行清理. (例如内存的释放交由操作系统来处理, 而不是程序自身).
>
> 如果你的程序binary文件想尽可能小, 则可以切换 panic 为 abort 处理模式. 在 Cargo.toml 文件中添加
>
> ```bash
> [profile.release]
> panic = 'abort'
> ```

跟踪栈. (此时, debug symbols 必须要开启. `--release` 参数构建的 binanry 则没有)

bash RUST_BACKTRACE=1 cargo run


## 可恢复错误 Result

rust

use std::fs::File;

fn main() { let f = File::open(“hello.txt”); let f = match f { Ok(file) => file, Err(error) => { panic!(“open file error {:?}“, 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(file) => file, Err(err) => { panic!(“create file error {}”, err); } }, _ => { panic!(“other error {}”, error); } } }; }


## 在 Error 中快速 panic

- `unwrap()` : 即如果 Result 是 Ok 则返回 Ok 持有的数据. 如果返回的是 Err 则调用 `panic!().` `let f = File::open("hello.txt").unwrap();`
- `expect(msg)` :  类似 unwrap, 但让我们选择 panic 输出的 message. 

## 传播 error

即将错误处理交由 caller 调用者来决定. 

rust fn read_line() -> Result {

}


快速传播 Error 操作符: `?`

rust fn read_string() -> Result { let mut f = File::open(“hello.txt”)?; let mut s = String::new(); f.read_to_string(&mut s)?; ok(s) }

//简化调用 fn read_string() -> Result { let mut s = String::new(); File::open(“hello.txt”)?.read_to_string(&mut s)?; Ok(s) } //再一步 use std::fs;

fn read_string() -> Result { fs::read_to_string(“hello.txt”) }


即在 `Result` 类型后添加 `?` .  如果返回 Ok, 则会继续执行. 否则 return 返回调用者. 

`?` 与 match 的区别是: `?` 会调用 `From trait` 的 `from` 函数, 该函数用于将 error 类型转换为另一个类型.

# 泛型, trait 以及生命周期

## 泛型函数

rust fn largest(list:&[T]) -> T {

}


## trait

> 定义共享行为

rust fn main() { let p = Person{}; println!(“{}”, p.say_hello()); }

//定义一个 trait pub trait TraitName { fn say_hello(&self) -> String;

fn say_hello2(&self) -> String { //添加默认实现 String::from(“default value”) } }

//为某类型实现 trait struct Person {

}

impl TraitName for Person { fn say_hello(&self) -> String { String::from(“hello world”) } }


### trait 作为参数

rust pub fn notify(item: impl TraitName) {

}

//或 pub fn notify(item: T) {

}

//多种 Trait 结合 pub fn notify(item: impl TraitName + Display) {

} //where clause fn notify(t: T, u: U) -> i32 where T: Display + Clone, U: Clone + Debug {

}


### trait 作为返回值

rust fn notify() -> impl TraitName {

}


## reference lifetime

每个引用的 lifetime , 即是它们的作用域. 大多数时候, lifetime 是隐含及推断的. 

> 它的主要目的是防止 dangling reference. 

lifetime annotation : 它并不改变一个引用能存活多久. 它只是描述了在多个 reference 之间的 lifetime 关系. 语法如下

rust &i32 , 添加 lifetime annotation 则为 &‘a i32, &‘a mut 32


单个 reference 标注 lifetime 并没有多大意义. 因为 annotation 主要是告诉 Rust 在多个 reference 之间的 lifetime 关系.

### 默认lifetime rules

- 每个reference 参数都有它们自己的 lifetime
- 如果仅有一个输入参数, 则输出(返回参数)的 lifetime 跟输入的一致.
- 如果有多个输入参数, 但其中之一是 `&self` 或 `&mut self` , 由于是 method, 则 `self` 的 lifetime 被应用于所有输出参数.

### static lifetime

有个特殊的 lifetime anntion , 它就是 `'static` . 所有 string 字面量都是这种 lifetime . 

## 它们组合在一起

rust fn longest_with<‘a, T>(x: &‘a str, y:&‘a str, ann: T) -> &‘a str where T: Display { println!(“ann {}”, ann); if x.len() > y.len() { x } else { y } }


# 自动化测试

rust pub mod hello { pub fn say_world(s: &str) { println!(“hello {}”, s); }

mod sub {
    fn say_world(s: &str) {
        println!("hello in sub mod {}", s);
        super::super::say_world_parent(s);
    }
}

}

use hello::say_world as h;

pub fn say_world_parent(s: &str) { h(s); }

#[cfg(test)] mod tests { #[test] fn testSay() { super::hello::say_world(“fuck the world”); assert_eq!(2 + 2, 5); } }


测试命令

bash

执行所有测试代码

cargo test

控制并行数

cargo test – –test-threads=1

只执行特定测试方法

cargo test 方法名

执行显式指定属性的测试方法

#[cfg(test)] mod tests { #[test] fn testSay() { super::hello::say_world(“fuck the world”); assert_eq!(2 + 2, 5); }

#[test]
#[ignore]
fn testSay2() {
    super::hello::say_world("fuck the world2");
    assert_eq!(2 + 2, 5);
}

} 这时 cargo test – –ignored 则只会执行带有 #[ignore] 属性的测试方法. 而普通 cargo test 则不会执行.


# I/O

## 读取命令行参数

rust use std::env;

fn main() { let args: Vec = env::args().collect(); println!(“? => {:?}“, args); } 注意, args() 如果包含有无效的 unicode 则会导致 panic. 如果想包含这种数据的话, 则要用 args_os() . 传递参数来运行 cargo run hello world ,则输出如下 Compiling hello-rust v0.1.0 (/Users/emacsist/Documents/rust/hello-rust) Finished dev [unoptimized + debuginfo] target(s) in 0.80s Running target/debug/hello-rust hello world [“target/debug/hello-rust”, “hello”, “world”]


# 函数式语言特征: iterator 与 closure

Closure : anonymous functions that can capture their environment . 即, 它可以捕获定义他们地方所有作用域的值.

rust let clo = |num| { println!(“in closure ….”); num }; //注意, let clo 表示 clo 包含匿名函数的定义, 则不是执行后的返回值.

//如果不指定参数类型, 则 Rust 以第一次调用时的为准.


fn trait

rust struct Cache where T: Fn(u32) -> u32 { exec: T, value: Option, }


## 捕获 environment

有三种方式, 对应函数参数的三种方式(take ownership, borrowing mutably, borrowing immutably). 三个 trait 如下

- `FnOnce` : 将会 take ownership. 
- `FnMut` : borrowing mutably
- `Fn` : borrowing immutably

可以显式指定 move : 

rust let x = 32; let clo = move |param| param == x 这时, x 的 ownership move 到了 closure 里了. 后面将不再有效


## iterator

> 注意, iter().next() 返回的是 &item 

所有 iterators 都是 lazy 的. 例如

rust let v1 = vec![1,2,3]; let v2 = v1.iter().map(|x| x + 1); // 这时 v2 是 Map 类型. 要调用 collect() 来消费 map 产生的 iter

use std::env; fn main() { let v1 = vec![1, 2, 3]; let v2: Vec = v1.iter().map(|x| x + 1).collect(); println!(“{:?}“, v2); }


它与 for 的性能: 在 Rust 中, 不用担心这些开销, iterator 与 for 几乎一样的.

# Cargo

默认的 profile 是 `dev` : 它的默认优化级别是 0. 

bash Cargo.toml 文件中

[profile.dev] opt-level = 0

[profile.release] opt-level = 3


文档化注释 `/// doc`

workspace : 

bash tree . . ├── Cargo.toml ├── hello │   ├── Cargo.toml │   └── src │   └── main.rs └── hello-lib ├── Cargo.toml └── src └── lib.rs

在 workspace 根目录执行命令 cargo build Compiling hello v0.1.0 (/private/tmp/work/hello) Compiling hello-lib v0.1.0 (/private/tmp/work/hello-lib) Finished dev [unoptimized + debuginfo] target(s) in 1.82s


从 cargo.io 安装 binary: `cargo install xxx`

# smart pointer

 ## `Box<T>`

用于指向在 heap 的数据. 它没有性能开销, 除了保存数据在 heap 而不是 stack 外.

### recursive type

在编译时, Rust 需要知道一种类型要占多少空间. 如果在编译时不知道大小的类型, 它就是一个 `recursive type` , 即该值是它自身同一类型的另一个值的一部分. 但 Box 有大小, 所以可以用它来实现 recursive type.

例如

rust enum List { Cons(i32, List), Nil, }

use List::{Cons,Nil}; let list = Cons(1, Cons(2, Cons(3, Nil)));


> 上面代码编译不了. 因为 Rust 编译时不知道 List 的大小.

借助 Box 就可以编译了

rust enum List { Cons(i32, Box), Nil, }

use List::{Cons,Nil}; let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));




通过 `Deref` trait , 让 smark pointer 与普通 reference 一样. 

通过实现 Deref trait , 允许你自定义 dereference operator, `*` . 

rust struct MyBox(T);

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

这时 let m = MyBox(13); *m 等同于 *(m.deref())




## Drop trait

当变量 owner 离开作用域时, Rust 会调用该变量的 `drop()` (如果有的话)来释放资源.

但该方法, 不允许显式调用. 例如 `o.drop();` . 但可以通过 `std::mem::drop` 来处理. `drop(o);`

## `Rc<T>`

变量默认是单个 ownership 的. 如果想开启多个 ownership, 则可使用该类型. 

注意, 它只能用于单线程环境.

只增加引用计数: `Rc::clone(&ref);` 如果调用 `ref.clone()` 这是deep copy 了

## `RefCell<T>`

它不同用 `Box<T> ` (编译期, compile time , 检查 borrow 规则), 而 `RefCell<T>` (运行期, runtime, 检查 borrow 规则)

它也只能用于单线程环境.

## 概括

- `Rc<T>` 允许同一份数据有多个 owner. 而 `Box<T>` 和 `RefCell<T>` 是 single owner
- `Box<T>` 允许 immutable 或 mutable borrow , 并且是在 compile time 检查. `Rc<T>` 只允许 immutable borrow  , 并且在 compile time 检查. `RefCell<T>` 允许 immutable 或 mutable borrow, 但是在 runtime 检查.
- 由于 `RefCell<T>` 允许 mutable borrow 并且在 runtime 才检查, 所以, 你可以更改在 `RefCell<T>` 的值, 即使 `RefCell<T>` 是 immutable 的也可以.

# 并发

在 Rust 的线程实现中, 与操作系统的是 `1:1` 模型.

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

fn main() { thread::spawn(|| { for i in 1..10 { println!(“hi {}, in thread”, i); thread::sleep(Duration::from_millis(1)); } }); for i in 1..5 { println!(“hi {}, in main”, i); thread::sleep(Duration::from_millis(1)); } }

//等待线程完成. hand 是 JoinHandle 类型. use std::thread; use std::time::Duration;

fn main() { let hand = thread::spawn(|| { for i in 1..10 { println!(“hi {}, in thread”, i); thread::sleep(Duration::from_millis(1)); } }); for i in 1..5 { println!(“hi {}, in main”, i); thread::sleep(Duration::from_millis(1)); } hand.join().unwrap(); }


##  move

> 这样, 可以让某线程从另一条线程数据中 move ownership.

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

fn main() {

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

let hand = thread::spawn(move || {
    println!("{:?}", v);
});

hand.join().unwrap();

for i in 1..5 {
    println!("hi {}, in main", i);
    thread::sleep(Duration::from_millis(1));
}

}


## 线程之间消息传递

> mpsc, multi producer, single consumer

rust

use std::thread; use std::time::Duration; use std::sync::mpsc;

fn main() { let (tx, rx) = mpsc::channel(); thread::spawn(move || { let msg = String::from(“hi”); tx.send(msg).unwrap(); });

let recMsg = rx.recv().unwrap();
println!("Got {}", recMsg);

}

//多个 tx let tx1 = mpsc::Sender::clone(&tx);


### channel 与 ownership

在 `tx` 发送消息时 `send()` 函数会 take ownership , 即参数的 ownership 会 move , 然后被传送到 rx.

## Mutex

rust use std::sync::Mutex;

fn main() { let m = Mutex::new(5);

{
    let mut num = m.lock().unwrap();
    *num = 6;
}
println!("{:?}", m);

}


## `Arc<T>`

类似 `Rc<T>` , 但是线程安全的. 

rust use std::sync::{Mutex, Arc}; use std::thread; use std::rc::Rc;

fn main() { //你可能已经看到, counter 是 immutable 的. 但后面却修改了它. // 这意味着 Mutex 提供了内部修改的能力. 类似 Cell 家族. let counter = Arc::new(Mutex::new(0));

let mut handles = vec![];
for _ in 1..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!("{:?}", *counter.lock().unwrap());

}


> 如果这里修改为 Rc , 则会导致编译错误.

# OOP

rust //定义 Object pub struct Person { name: String, age: u16, }

//定义方法 impl Person { pub fn getName(&self) -> &str {

} }

// Trait pub trait Run { fn run(&self); }

//为 Person 实现 Run trait impl Run for Person { fn run(&self) { //code… } }




# 常见问题及解决

## Blocking waiting for file lock on package cache

bash rm ~/.cargo/.package-cache 然后重新 cargo run ```