Skip to content

Rust 初始

· 11 min

Rust#

具体介绍看一下 百度百科

简单来说,和 C++ 和 C 一样没有 GC, 但是又不用去管理内存的编程语言。

学习文档#

https://kaisery.github.io/trpl-zh-cn/ch20-01-single-threaded.html https://course.rs/about-book.html

Cargo 包管理工具#

Terminal window
cargo new helll__cargo # 新建项目
cargo build # 编译项目
cargo build --release # 优化编译项目
cargo run # 编译并运行项目
cargo check # 检查是否能通过编译,并不生成可执行文件
[package]
name = "hello_cargo"
version = "0.1.0"
edition = "2021"
[dependencies]

match#

match 会开始一个新作用域

let dice_roll = 9
match dice_roll {
1 | 2 => println!("one or two"),
3 => add_fancy_hat(),
4..=6 => println!("one through five"), // 匹配 4 <= x <= 6
7 => remove_fancy_hat(),
other => move_player(other), // other 会匹配 3 和 7 之外的所有值
}
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
_ => reroll(), // _ 匹配任意值而不绑定到该值
}

return 写不写#

https://cloud.tencent.com/developer/article/1559616

fn r(n: i32) -> i32 {
if n > 0 {
0
}
1
}

编译不通过, expected (), found integer

原因:

rust 编译器认为函数体是由一个语句和表达式组成的 if n > 0 { 0 } 和 1 构成的。

因为没有 else,所以它不认为 if 代码块是表达式的一部份 所以做为函数返回值的是 1 这个表达式

第一个语句中的代码块的最后一行是表达式,但其它本身又不是条件赋值语句。另外 if 作为 rust 的表达式,要求 if 和 else 两部分类型相同,如果缺少 else 部分,else 部分默认是 (), 这样就要求 if 部分求值结果也是()。

解决方法:

  1. 把 1 放进 else 里面。
  2. 在 0 前面显式的加上 return。

生命周期#

// 没有 'a 时,函数不知道返回值的引用会不会返回过期的引用,为了保障自己传递出去的值是正常的,需要添加相同的生命周期告诉编译器。
fn longer<'a>(s1: &'a str, s2: &'a str) -> &'a str {
if s2.len() > s1.len() {
s2
} else {
s1
}
}
fn main() {
let r;
{
let s1 = "rust";
let s2 = "ecmascript";
r = longer(s1, s2); // s1,s2 已经 copy 给了 r。
}
println!("{} is longer", r);
}

#

https://www.bookstack.cn/read/DaseinPhaos-tlborm-chinese/README.md

macro_rules!#

宏选择器

macro_rules! yo {
($name: expr) => {
println!("Yo {}!", $name);
}
}
//TODO
// macro_rules! hei {
// ($($name: expr), *) => {
// println!("Hei {}!", $name);
// }
// }
// hei!("Finn", "Jake", "PB");
fn main() {
yo!("Finn");
}

#

syn 解析库

quote 生成库

&

Rust含义
a: &T都不能修改
mut a:&T不能修改 a 指向的内容
a: &mut T不能修改 a
mut a:&mut T都能修改

https://zhuanlan.zhihu.com/p/189337028

dyn#

dyn 是 trait 对象类型的前缀,dynamic 的缩写。比如你有一个 trait 名字叫做 Writable,你可以用 dyn Writable 表示实现了 Writable 特征的 struct。

dyn 关键字用于强调调用相关特征的方法是动态分配的,即运行时才知道调用的是哪个实现了特征的结构体(类似 java 的运行时多态)。

不像范型参数或者 impl Trait,编译器无法知道调用的时候被传递的具体类型是什么。也就是说,类型信息被擦除了。就其本身而言,一个 dyn Trait 引用包含两个指针。一个指针指向数据(比如一个结构体的实例)。另一个指针指向方法名与函数指针的 map(也叫做虚方法表)。

在运行时,当需要调用 dyn Trait 上的方法的时候,会查询虚方法表以获取函数指针,然后去调用查找到的函数指针。

使用 dyn Trait 的一个例子:

fn main() {
let mut personList: Vec<Box<dyn Role>> = Vec::new();
let student = Student;
let teacher = Teacher;
personList.push(Box::new(student));
personList.push(Box::new(teacher));
for p in personList {
p.print_role();
}
}
struct Student;
struct Teacher;
trait Role {
fn print_role(&self);
}
impl Role for Student {
fn print_role(&self) {
println!("role is student");
}
}
impl Role for Teacher {
fn print_role(&self) {
println!("role is teacher");
}
}

参考 https://doc.rust-lang.org/std/keyword.dyn.html

https://blog.csdn.net/lzufeng/article/details/125728599

异步#

异步库:tokio, soml

资料 https://www.modb.pro/db/248295

https://www.freesion.com/article/5043318152/

Fn FnMut FnOnce#

闭包被分为了三种类型,列举如下

Box Rc Arc Cell RefCell 智能指针#

Box#

Box<T> 指向在类型为 T 的堆上分配的数据。Box<T> 允许将数据存储在堆而不是栈上。

Rc Arc#

Rc(reference counting 引用计数) 是一种共享所有权智能指针。克隆了一份智能指针 Rc,并将该智能指针的引用计数增加到 2。适用于单线程

Arc(Atomic Rc 原子引用计数) 是一种线程安全的共享所有权智能指针。适用于多线程(由于 Rc<T> 需要管理引用计数,但是该计数器并没有使用任何并发原语,因此无法实现原子化的计数操作,最终会导致计数错误。)

Rc 与 Arc

Cell RefCell#

Cell 和 RefCell 在功能上没有区别,区别在于 Cell<T> 适用于 T 实现 Copy 的情况

use std::cell::Cell;
fn main() {
let c = Cell::new("asdf");
let one = c.get();
c.set("qwer"); // 不写 mut 也可以修改
let two = c.get();
println!("{},{}", one, two);
}

Cell 是一种提供内部可变性的容器,Cell 没有额外的性能损耗。

RefCell 是一种提供内部可变性的容器

Cell 和 RefCell

Cow#

Cow 是一种写时复制的枚举体的智能指针

Deref 解引用#

use std::ops::Deref;
struct MyBox<T>(T);
impl<T> Deref for MyBox<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[test]
fn main() {
let my = MyBox("ss");
/// `MyBox` 没有实现 `to_string()` 也可以调用
my.to_string();
}

Drop 释放资源#

结构体#

标准的结构体 —— C struct

struct Person {
name: String,
age: i32,
address: String,
}

简略的结构体 —— tuple struct

struct Person(String, i32, String);

最简略的结构体 —— unit struct

struct Person;

字符串格式化#

format!("Hello"); // => "Hello"
format!("Hello, {}!", "world"); // => "Hello, world!"
format!("The number is {}", 1); // => "The number is 1"
format!("{:?}", (3, 4)); // => "(3, 4)"
format!("{value}", value=4); // => "4"
let people = "Rustaceans";
format!("Hello {people}!"); // => "Hello Rustaceans!"
format!("{} {}", 1, 2); // => "1 2"
format!("{:04}", 42); // => "0042" with leading zeros
format!("{:#?}", (100, 200)); // => "(
// 100,
// 200,
// )"
// All of these print "Hello x !"
println!("Hello {:5}!", "x");
println!("Hello {:1$}!", "x", 5);
println!("Hello {1:0$}!", 5, "x");
println!("Hello {:width$}!", "x", width = 5);
let width = 5;
println!("Hello {:width$}!", "x");

小数 Precision#

// Hello {arg 0 ("x")} is {arg 1 (0.01) with precision specified inline (5)}
println!("Hello {0} is {1:.5}", "x", 0.01);
// Hello {arg 1 ("x")} is {arg 2 (0.01) with precision specified in arg 0 (5)}
println!("Hello {1} is {2:.0$}", 5, "x", 0.01);
// Hello {arg 0 ("x")} is {arg 2 (0.01) with precision specified in arg 1 (5)}
println!("Hello {0} is {2:.1$}", "x", 5, 0.01);
// Hello {next arg -> arg 0 ("x")} is {second of next two args -> arg 2 (0.01) with precision
// specified in first of next two args -> arg 1 (5)}
println!("Hello {} is {:.*}", "x", 5, 0.01);
// Hello {arg 1 ("x")} is {arg 2 (0.01) with precision
// specified in next arg -> arg 0 (5)}
println!("Hello {1} is {2:.*}", 5, "x", 0.01);
// Hello {next arg -> arg 0 ("x")} is {arg 2 (0.01) with precision
// specified in next arg -> arg 1 (5)}
println!("Hello {} is {2:.*}", "x", 5, 0.01);
// Hello {next arg -> arg 0 ("x")} is {arg "number" (0.01) with precision specified
// in arg "prec" (5)}
println!("Hello {} is {number:.prec$}", "x", prec = 5, number = 0.01);

https://doc.rust-lang.org/stable/std/fmt/

GAT#

GAT 其实是 Generic Associated Types 的缩写, 即泛型关联类型。

https://zhuanlan.zhihu.com/p/580996117

https://zhuanlan.zhihu.com/p/463367405

https://www.zhihu.com/question/463506409/answer/2788271435

chrono#

时间库

格式化#

SpecExampleDescription
%Y2001年份
%C20年份的高两位
%y01年份的低两位
%m07月份
%bJul月份 3 个字母
%d08
%aSun星期
%H0024 制小时
%M34分钟
%S60

https://docs.rs/chrono/latest/chrono/format/strftime/index.html

trait#

ParitalOrd / Ord / ParitalEq / Eq#

对于集合 X 中的元素 a, b, c,

  1. 如果 a < b 则一定有 !(a > b);反之,若 a > b,则一定有 !(a < b),称为反对称性。
  2. 如果 a < b 且 b < c 则 a < c,称为传递性。
  3. 对于 X 中的所有元素,都存在 a < b 或 a > b 或者 a == b,三者必居其一,称为完全性。

对于 X 中的元素具备上述前两特征称为偏序,同时具备所有特征称为全序。

ParitalOrd 和 ParitalEq 为偏序

因为浮点数中有特殊的值 NaN,所有浮点数不具备全序特征。

PhantomData<T> 幽灵数据#

零大小类型的标记结构体

未使用的生命周期#

struct Slice<'a, T> {
start: *const T,
end: *const T
}

不允许存在声明 'a, 但未使用。修改如下:

struct Slice<'a, T: 'a> {
start: *const T,
end: *const T,
_marker: PhantomData<&'a T>
}

未使用的类型#

struct SendCh<T, C: Sender<T>> {
ch: C,
name: &'static str,
marker: PhantomData<T>
}

Rust之PhantomData

Option enum and Error enum#

Option and_then#

pub const fn and_then<U, F>(self, f: F) -> Option<U>
where
F: FnOnce(T) -> Option<U> + Destruct
assert_eq!(Some(1).and_then(|_| Some(2)), Some(2))

对 Some 里的值进行变换,并扁平

Option map#

pub const fn map<U, F>(self, f: F) -> Option<U>
where
F: ~const FnOnce(T) -> U,
F: ~const Destruct,
assert_eq!(Some(1).map(|_| 2), Some(2))

对 Some 里的值进行变换

Option unwrap_or#

pub const fn unwrap_or(self, default: T) -> T
where
T: ~const Destruct
assert_eq!(Some("car").unwrap_or("bike"), "car");
assert_eq!(None.unwrap_or("bike"), "bike");

为 None 使用默认值

Option unwrap_or_else#

pub const fn unwrap_or_else<F>(self, f: F) -> T
where
F: ~const FnOnce() -> T,
F: ~const Destruct

为 None 使用函数生成默认值

Option unwrap_or_default#

pub const fn unwrap_or_default(self) -> T
where
T: ~const Default

自动使用 Default::default() 生成默认值