Rust 学习笔记

97 阅读9分钟

Rust 学习笔记

这是一份系统的 Rust 学习笔记,涵盖了从基础语法到高级特性的内容。

📋 目录

环境配置

镜像设置

# 安装 crm 来设置镜像
cargo install crm

# 测试最快镜像
crm test

# 自动设置最快镜像
crm best

基础语法

1. 输出与格式化

// 使用 {} 自动占位
println!("{}", a);

// "" 代表字符串,'' 代表字符

2. 文档注释

/// 文档注释 
/// # Examples
//! 为包(crates)或者模块添加文档注释

// 配合 cargo doc --open 运行并预览

3. 表达式与返回值

  • 隐式表达式不能包含分号: x + y
  • 如果显式的 return 则无所谓
  • 函数没有返回值,默认返回 ()
  • 表达式以分号 ; 结尾,比如 x + y;,返回 ()

4. 数据类型

类型描述范围
u32无符号整型,32位只有正数
i32有符号整型,32位-(2^n - 1) ~ 2^(n-1) - 1
isize/usize由计算机决定-
f32浮点类型默认 f64,精度更高

5. 永不返回类型

// 用感叹号 ! 作为返回类型,表示该函数永不返回(diverging functions)
fn dead_fn() -> ! {
    panic!("你已经到了穷途末路,崩溃吧!");
}

6. Copy 类型

可以 copy 的类型(赋值后原变量仍可用):

  1. 所有整数类型(如 u32
  2. 布尔类型 bool
  3. 所有浮点数类型(如 f64
  4. 字符类型 char
  5. 元组,当且仅当其包含的类型也都是 Copy 的时候
  6. 不可变引用 &T

7. 变量与常量

// 变量默认不可变, 使用lei关键字
let mut s = 6;  // 添加 mut 使变量可变

// 常量, 使用const关键字, 不可以使用mut, 因为常量不可变
const MAX_POINT: u32 = 100;

// 引用
&符号表示引用:允许你引用某些值而不取得所有权
&mut:可变借用

// 静态变量
static HELLO_WORLD: &str = "Hello world!";

// 可变静态变量需要 unsafe
static mut COUNTER: u32 = 0;

fn add(inc: u32) {
    // 凡是访问或修改可变的静态变量要放在unsafe块中
    unsafe {
        COUNTER += inc;
    }
}

所有权系统

8. 内存管理

  • 栈内存 stack
  • 堆内存 heap
所有权规则
  1. 每个值都有一个变量,这个变量为该值的所有者
  2. 每个值同时只能有一个所有者
  3. 当所有者超出作用域时,该值将被删除
借用规则
  • 可变引用同时只能存在一个
  • 可变引用与不可变引用不能同时存在
// 错误示例:同一时间无法对 `s` 进行两次可变借用
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s; // Error

// 错误示例:无法借用可变 `s` 因为它已经被借用了不可变
let mut s = String::from("hello");
let r1 = &s;      // 没问题
let r2 = &s;      // 没问题  
let r3 = &mut s;  // 大问题

// 综上,同一时刻,你只能拥有要么一个可变引用,要么任意多个不可变引用

9. 切片

// 切片就是对 String 类型中某一部分的引用
let s = String::from("hello world");

// [开始索引, 结束索引) - 左闭右开
let hello = &s[0..5];
let world = &s[6..11];

// 省略前后,等于完整切片
let whole = &s[..];

// 字符串切片的类型标识是 &str
let s: &str = "Hello, world!";
字符串拼接
// 方法1: push_str
let mut s = String::from("foo");
s.push_str("bar");  // "foobar"

// 方法2: push(单个字符)
s.push('l');

// 方法3: 加号+ ,加号类似于fn add(self, s: $Str) -> String{}
let s1 = String::from("hello");
let s2 = String::from("world");
let s3 = s1 + &s2;  // 注意:s1 的所有权被转移

// 方法4: format!(推荐),format!不会取得所有权
let s = format!("{}{}", s2, s3);

复合类型

10. 元组 Tuple

let tup: (i32, f64, u8) = (500, 6.4, 1);

// 解构
let (x, y, z) = tup;

// 访问元素
let first = tup.0;

11. 结构体 struct

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

// 创建实例
// 如果结构体实例是可变的,那么实例中所有字段都是可变的
let mut user1 = User {
    email: String::from("someone@example.com"),
    username: String::from("someusername123"),
    active: true,
};

// 元组结构体
struct Point(i32, i32, i32);
let origin = Point(0, 0, 0);

12. 枚举 enum

// 定义扑克牌花色枚举
enum PokerSuit {
    Clubs,
    Spades,
    Diamonds,
    Hearts,
}

let heart = PokerSuit::Hearts;

// Option 枚举用于处理空值
// 解释就是:一个变量要么有值:Some(T), 要么为空:None
enum Option<T> {
    Some(T),
    None,
}

// 为枚举定义方法, 也是使用impl关键字
enum Message {
    Write(String),
    Move(i32, i32),
}

impl Message {
    fn call(&self) {}
}

13. 数组

固定长度的数组叫Array, 动态数组叫Vector

// 固定长度数组
let a: [i32; 5] = [1, 2, 3, 4, 5];

// 初始化重复值
// [类型; 长度] 或者 [初始值; 长度]
let a = [3; 5];  // 3位初始值;长度为5, 所以就等同于[3, 3, 3, 3, 3]

// 数组切片
let slice: &[i32] = &a[1..3];

控制流

14. 条件与循环

// if 表达式
if condition {
    // code
} else {
    // code
}

// for 循环
for item in collection {
    // 转移所有权
    // 等价使用方式 for item in IntoIterator::into_iter(collection)
}

for item in &collection {
    // 不可变借用
    // 等价使用方式for item in collection.iter()
}

for item in &mut collection {
    // 可变借用
    // 等价使用方式 for item in collection.iter_mut()
}

// Range 循环
for number in (1..4).rev() { 
    // (1..4)是标准库的Range写法, 即从1到4,但是不包含结束
    // rev方法是用于反转Range的
    println!("{}", number);  // 3 2 1
}

// loop 循环
// 例1
loop {
    if n > 5 {
        break
    }
    println!("{}", n);
} 
// 例2
// loop 是一个表达式,因此可以返回一个值     
let result = loop {
    counter += 1;
    if counter == 10 {
        break counter * 2;  //break 可以单独使用,也可以带一个返回值,有些类似 return
    }
};

模式匹配

15. match 表达式

match target {
    模式1 => 表达式1,
    模式2 | 模式3 => {
        语句1;
        语句2;
        表达式2
    },
    模式4..=模式5 => 表达式3,  // 匹配范围
    _ => 表达式4  // 通配符
}
if let 简写

记住:当你只要匹配一个条件,且忽略其他条件时就用 if let ,否则都用 match。

// 单个匹配
if let Some(x) = v {
    println!("three");
}

// 带 else 的写法
let Some(x) = six else {
    println!("No result");
    return;
};
忽略值
let numbers = (2, 4, 8, 16, 32);

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

16. @绑定

// @符号用于创建一个变量,用于在模式匹配成功后,将值绑定这个变量
fn test_id(id: i32) {
    match id {
        x @ 1..=5 => println!("{}", x),  // 绑定匹配值到 x
        _ => println!("other id")
    }
}

面向对象

17. 方法 Method

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

impl Rectangle {
    // 方法 - 第一个参数是 &self
    fn area(&self) -> u32 {
        self.width * self.height
    }
    
    // 关联函数 -> 在impl块里定义不把self作为第一个参数的函数, 不是方法, 用:::调用,例如String::from()
    fn square(size: u32) -> Rectangle {
        Rectangle {
            width: size,
            height: size
        }
    }
}

let rect1 = Rectangle { width: 30, height: 50 };
let area = rect1.area();

// 调用关联函数, 用::调用
let s = Rectangle::square(20);

泛型与特征

18. 泛型

// 泛型结构体
struct Point<T, U> {
    x: T,
    y: U,
}

let p = Point { x: 1, y: 1.1 };

19. 特征 Trait

// 定义特征, 类似于接口
pub trait Summary {
    fn summarize(&self) -> String;
}

// 实现特征
pub struct Weibo {
    pub username: String,
    pub content: String
}

impl Summary for Weibo {
    // 在Weibo的实例上使用.summarize方法
    fn summarize(&self) -> String {
        format!("{}发表了微博{}", self.username, self.content)
    }
}
特征约束
// 特征约束
// 形如 T: Summary 被称为特征约束,约束了泛型为具体类型。
pub fn notify<T: Summary>(item: &T) {
    println!("Breaking news! {}", item.summarize());
}
同名方法处理
trait Pilot {
    fn fly(&self);
}

struct Human;

impl Pilot for Human {
    fn fly(&self) {
        println!("Pilot Speaking")
    }
}

impl Human {
    fn fly(&self) {
        println!("Human Speaking")
    }
}

let person = Human;
person.fly();          // "Human Speaking", 这里没有指定trait, 默认调用自身
Pilot::fly(&person);   // "Pilot Speaking"
Supertrait

即一个trait要继承其他trait的功能

use std::fmt;

// 实现Outline, 也必须实现Display这个特征
// 也可以说Outline依赖Display
trait Outline: fmt::Display {
    fn outline_print(&self) {
        let output = self.to_string(); // 因为to_string()这个方法,所以必须实现Display这个特征
        let len = output.len();
        println!("{}", "*".repeat(len + 2));
    }
}

struct Point {}

// 实现Outline 
impl Outline for Point {}

// 这里还必须实现Display, 不然会报错
impl fmt::Display for Point {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    // ...
  }
}

20. 特征对象

特征对象可以通过 & 引用或者 Box<T> 智能指针的方式来创建。

// 在 Rust 中,有两个self,一个指代当前的实例对象,一个指代特征或者方法类型的别名:
trait Draw {
    fn draw(&self) -> Self;
}

集合类型

21. 动态数组 Vector

// 创建 Vector
let v: Vec<i32> = Vec::new();
let mut v = Vec::new();
v.push(1);

let v = vec![1, 2, 3];           // 使用宏创建
let v = vec![0; 3];              // 默认值 0,长度 3
let v_from = Vec::from([0, 0, 0]);
访问元素
// 方式1: 下标(可能 panic)
let third: &i32 = &v[2];

// 方式2: get 方法(安全)
// 使用get,它返回了 Option<&T>,因此还需要额外的 match 来匹配解构出具体的值
match v.get(2) {
    Some(third) => println!("第三个元素是 {third}"),
    None => println!("去你的第三个元素,根本没有!"),
}
存储枚举类型
let row = vec![
    enumName::Int(2),
    enumName::Text(String::from("blue")),
];
遍历 Vector
let v = vec!['a', 'b', 'c'];
// v.iter().enumerate()返回元祖形式
for (index, value) in v.iter().enumerate() {
    println!("{}, {}", index, value);
}

22. HashMap KV存储

use std::collections::HashMap;

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

// 获取值
let team_name = String::from("Blue");
let score: Option<&i32> = scores.get(&team_name);

// 如上
// 1 .get 方法返回一个 Option<&V> 类型的值,其中 V 是集合中值的类型。若找到了对应的键,就返回 Some(&value);若没找到,就返回 None。
// 2. get 方法的 key 参数必须是一个引用

// 安全获取值
// 若找到对应分数,就将其赋值给变量 score;若未找到,score 则会被赋值为 0
// copied 方法的作用是把 Option<&T> 转换为 Option<T>,也就是把引用类型转换为值类型。若 Option 是 Some(&value),就返回 Some(value);若 Option 是 None,则返回 None。
// 若 Option 是 Some(value),unwrap_or 方法会返回 value;若 Option 是 None,则返回传入的默认值,这里的默认值是 0
let score: i32 = scores.get(&team_name)
    .copied()
    .unwrap_or(0);

遍历 HashMap
for (k, v) in &scores {
    println!("{}:{}", k, v);
}
entry 方法
// 检查 key 对应的 value 是否存在,不存在则插入
map.entry(key).or_insert(default_value);

生命周期

23. 生命周期注解

// 生命周期参数需要先声明 <'a>
// 这里x、y 和返回值至少活得和 'a 一样久(因为返回值要么是 x,要么是 y)
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
生命周期规则
  • 每个引用类型的参数都有自己的生命周期
  • 如果只有1个输入生命周期参数,该生命周期被赋予所有输出生命周期参数
  • 如果有多个输入生命周期参数,其中一个是 &self&mut self,则 self 的生命周期被赋予所有输出生命周期参数
静态生命周期

拥有该生命周期的引用可以和整个程序活得一样久。

let s: &'static str = "我没啥优点,就是活得久,嘿嘿";

错误处理

24. panic! 不可恢复错误

panic!("crash and burn");

25. Result 可恢复错误

// Result 枚举定义
enum Result<T, E> {
    Ok(T),
    Err(E),
}
文件读取示例

以下函数从文件中读取用户名,然后将结果进行返回:

use std::fs::File;
use std::io::{self, 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),
    }
}

// 使用 ? 简写
// ? 就是一个宏,它的作用跟上面的 match 几乎一模一样
// ? 只能用于返回Result的函数, 比如下面函数最后返回了Ok()
fn read_username_from_file() -> Result<String, io::Error> {
    let mut f = File::open("hello.txt")?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}

// 最简写法
fn read_username_from_file() -> Result<String, io::Error> {
    std::fs::read_to_string("hello.txt")
}

模块系统

26. 包和模块

基本概念
  • Package: 软件包
  • Crate: 单元包
  • Module: 模块
Crate 类型
  1. Binary: 二进制可执行文件
  2. Library: 库文件
Package 结构

使用 cargo new package_name 创建一个 package:

  • 包含一个 Cargo.toml,描述如何构建 crates
  • 至少包含一个 crate(library 或 binary)
  • 最多只能有一个 library crate(src/lib.rs
  • 可以有多个 binary crate(src/bin/ 目录下)
模块定义
// 使用 mod 关键字创建模块
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

// 绝对路径
// 绝对路径引用时,从包根开始,路径名以包名或者 crate 作为开头, 可以直接以 crate 开头,然后逐层引用,每一层之间使用 :: 分隔:
use crate::front_of_house::hosting;

// 相对路径
// 相对路径,从当前模块开始,以 self,super 或当前模块的标识符作为开头, super即上级目录
// super 代表的是父模块为开始的引用方式,例如: super::serve_order();
// self 其实就是引用自身模块中的项
use self::front_of_house::hosting;
use super::some_module;  // 上级模块
结构体和枚举的可见性
  • pub 关键字控制模块和项的可见性
  • 结构体设置为 pub 时,字段依然是私有的
  • 枚举设置为 pub 时,所有字段对外可见

27. use 引入模块

引入函数的时候, 一般引用的是函数的父级模块, 这是惯用做法,目的是区分这是模块引入的,还是本地定义的 但是对于struct、enum, 一般指定到本身

基本用法
use std::collections::HashMap;
use std::fmt::Result;
use std::io::Result as IoResult;  // 别名
简化引入
// 分组引入
use std::collections::{HashMap, BTreeMap, HashSet};
use std::{cmp::Ordering, io};

// self 引入
use std::io::{self, Write};

// 通配符引入(谨慎使用)
use std::collections::*;

高级特性

28. 格式化输出

基本输出宏
  • print!: 输出到标准输出,不带换行符
  • println!: 输出到标准输出,带换行符
  • format!: 输出到 String
占位符区别
  • {}: 适用于实现了 std::fmt::Display 的类型
  • {:?}: 适用于实现了 std::fmt::Debug 的类型(调试场景)
高级格式化
// 位置参数
println!("{1}{0}", 1, 2);  // "21"
println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob"); // => Alice, this is Bob. Bob, this is Alice

// 具名参数
println!("{argument}", argument = "test");
println!("{name} {}", 1, name = 2); // => "2 1"

// 精度控制
let v = 3.1415926;
println!("{:.2}", v);      // 保留小数点后两位 => 3.14
println!("{:+.2}", v);     // 带符号保留小数点后两位 => +3.14
println!("{:.0}", v);      // 不带小数 => 3
println!("{:.1$}", v, 4);  // 通过参数来设定精度 => 3.1416,相当于{:.4}

29. 闭包

基本语法
// 完整形式
|param1, param2| {
    语句1;
    语句2;
    返回表达式
}

// 简写形式
|param| 返回表达式 // 如果只有一个返回表达式
捕获方式

闭包捕获变量有三种途径:

特征描述所有权
FnOnce转移所有权所有闭包自动实现
FnMut可变借用没有移出所有权的闭包
Fn不可变借用不需要改变捕获变量的闭包
使用建议
  • 先使用 Fn 特征
  • 编译器会提示正确的特征选择

30. 迭代器 Iterator

基本用法
let arr = [1, 3];
let mut arr_iter = arr.into_iter();

assert_eq!(arr_iter.next(), Some(1));
assert_eq!(arr_iter.next(), Some(3));
assert_eq!(arr_iter.next(), None);
迭代器适配器
// 消耗型适配器:collect
let v1: Vec<_> = arr.iter().map(|x| x + 1).collect();
所有权关系
使用方法等价方式所有权
for item in collectionIntoIterator::into_iter(collection)转移所有权
for item in &collectioncollection.iter()不可变借用
for item in &mut collectioncollection.iter_mut()可变借用

31. 类型转换

let a = 3.1 as i8;      // 浮点转整数
let b = 100_i8 as i32;  // 整数扩展
let c = 'a' as u8;      // 字符转整数

32. 线程

使用 thread::spawn 可以创建线程

创建线程
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));
    }
});
重要概念
  • thread::spawn 返回 JoinHandle, JoinHandle持有值的所有权
  • JoinHandle::join() 会阻塞当前线程直到子线程结束
  • 使用 move 关键字将变量所有权转移给新线程
  • 主线程结束时,所有子线程都会被终止
  • 使用thread::spawn来创建线程,创建出的多个线程之间并不存在执行顺序关系,因此代码逻辑千万不要依赖于线程间的执行顺序。
  • 实现Send trait的类型可在线程间转移所有权
所有权转移示例
let v = vec![1, 2, 3];

let handle = thread::spawn(move || {
    println!("Here's a vector: {:?}", v);
});

handle.join().unwrap();

33. 消息传递Channel

使用 mpsc::channel 函数来创建Channel,返回一个元祖: (发送端,接收端)

use std::sync::mpsc;
use std::thread;

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

thread::spawn(move || {
    let val = String::from("hi");
    tx.send(val).unwrap();
    // 注意:val的所有权已经被转移
});

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

34. Mutex互斥锁

重要原则:

  1. 在使用数据之前,必须尝试获取锁(lock)
  2. 使用完mutex所保护的数据,必须对数据进行解锁,以便其他线程可以获取锁
use std::sync::{Arc, Mutex};
use std::thread;

let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];

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

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

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

重要对比

  • 我们使用 RefCell<T> 来改变 Rc<T> 里面的内容
  • 我们使用 Mutex<T> 来改变 Arc<T> 里面的内容
  • ⚠️ Mutex<T> 有死锁风险

35. 错误处理进阶

or() 和 and() 操作

  • or():表达式按照顺序求值,若任何一个表达式的结果是 SomeOk,则该值会立刻返回

    • Some1 or Some2 = Some1
    • Err or Ok = Ok
  • and():若两个表达式的结果都是 SomeOk,则第二个表达式中的值被返回。若任何一个的结果是 NoneErr,则立刻返回。

    • Some1 and Some2 = Some2
    • Err and Ok = Err
    • Err1 and Err2 = Err1
let s1 = Some("hello");
let s2 = Some("world");
let n: Option<&str> = None;

assert_eq!(s1.or(s2), s1);  // Some("hello")
assert_eq!(s1.and(s2), s2);  // Some("world")
assert_eq!(n.or(s2), s2);    // Some("world")

36. Macro 宏

宏类型区别

  • derive宏:只能用于struct和enum
  • 属性宏:可以用于任意条目,比如函数

常用宏示例

// derive宏
#[derive(Debug, Clone, PartialEq)]
struct Point {
    x: i32,
    y: i32,
}

// 属性宏
#[test]
fn it_works() {
    assert_eq!(2 + 2, 4);
}

// 声明宏
macro_rules! vec_of_strings {
    ($($x:expr),*) => (vec![$($x.to_string()),*]);
}

let v = vec_of_strings!["hello", "world"];

37. async/await 异步编程

use futures::executor::block_on;

async fn hello_world() {
    hello_cat().await;
    println!("hello, world!");
}

async fn hello_cat() {
    println!("hello, kitty!");
}

fn main() {
    let future = hello_world();
    block_on(future);
}

备注:需要添加 futures = "0.3" 到 Cargo.toml

异步运行时对比

运行时特点
tokio功能最全面,生态最成熟
async-std标准库的异步版本
smol轻量级,模块化设计

38. test

测试类型

  • 单元测试:需要在文件头部添加 #[cfg(test)]
  • 集成测试:不需要,但需要单独目录,目录名为 tests

测试函数标注

测试函数需要使用test属性(attribute)进行标注:

  • 在函数上加 #[test],可把函数变成测试函数
  • Attribute就是一段Rust代码元数据

运行测试

cargo test                    # 运行所有测试
cargo test fn_name           # 指定测试单个测试函数
cargo test filter_name       # 模糊匹配名称开头的多个函数

断言宏

assert!(bool);                // 接受一个返回bool的参数,为true通过,为false则panic
assert_eq!(target, compare);  // 判断两者相等
assert_ne!(target, compare);  // 判断两者不等

// 自定义错误信息
assert!(is_able(), "这里可能为false");

忽略测试

#[test]
#[ignore]
fn expensive_test() {
    // 需要较长时间运行的测试
}

39. 工作空间workspace

在外层 Cargo.toml 中申明 workspace:

[workspace]
members = [
    "lib-one",
    "lib-two"
]

工作空间命令

cargo test -p lib-one    # 单独运行测试某个包
cargo build            # 构建工作空间下所有包
cargo test             # 运行工作空间下所有包的测试

目录结构示例

my-workspace/
├── Cargo.toml          # workspace根配置
├── lib-one/
│   ├── Cargo.toml
│   └── src/lib.rs
├── lib-two/
│   ├── Cargo.toml
│   └── src/lib.rs
└── tests/              # 集成测试
    └── integration_test.rs

40. 解引用*

基本用法

let x = 5;
let y = &x;

assert_eq!(5, *y); // *是解引用符号,用于取得引用指向的值

备注:不加*,会报错:整数与整数引用不可比较

Box解引用

let x = 5;
let y = Box::new(x);

assert_eq!(5, x);
assert_eq!(5, *y);

自定义解引用

use std::ops::Deref;

struct MyBox<T>(T);

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

let x = 5;
let y = MyBox(x);

assert_eq!(5, *y);  // 通过Deref trait实现

41. unsafe

使用 unsafe 关键字,切换到 unsafe rust,开启一个 unsafe 块,存放 unsafe 代码

unsafe 超能力

  • 解引用原始指针
  • 调用 unsafe 函数或方法
  • 访问或修改可变的静态变量
  • 实现 unsafe trait

原始指针

let mut num = 5;

let r1 = &num as *const i32;  // 不可变原始指针
let r2 = &mut num as *mut i32;  // 可变原始指针

unsafe fn dangerous() {}

// unsafe块 
unsafe {
    println!("r1: {}", *r1);
    println!("r2: {}", *r2);
    
    dangerous(); // unsafe方法需要在块里被调用
}

实际用例

// 与C语言交互
extern "C" {
    fn abs(input: i32) -> i32;
}

unsafe {
    println!("Absolute value of -3 according to C: {}", abs(-3));
}

42. 类型别名 type

可减少代码字符重复

type Result<T> = Result<T, std::io::Error>;

fn write(buf: &[u8]) -> Result<usize> {
    // 这里返回直接用 Result<T>, 而不需要写完整了
    Ok(buf.len())
}

// 更复杂的别名
type Thunk = Box<dyn Fn() + Send + 'static>;
type IoResult<T> = Result<T, std::io::Error>;

标准库中的类型别名

// std::io 中定义
type Result<T> = Result<T, Error>;
type IoResult<T> = Result<T, Error>;

// std::collections 中定义
type HashMap<K, V> = std::collections::HashMap<K, V>;

持续修改和补充