pull/427/head
sunface 3 years ago
parent d864d97281
commit 84b33f4dba

@ -50,23 +50,22 @@
- [返回值和错误](basic/result-error/intro.md)
- [panic深入剖析!](basic/result-error/panic.md)
- [返回值Result和?](basic/result-error/result.md)
- [包和模块](basic/crate-module/intro.md)
- [包crate](basic/crate-module/crate.md)
- [模块Module](basic/crate-module/module.md)
- [使用use引入模块及受限可见性](basic/crate-module/use.md)
- [注释和文档](basic/comment.md)
- [格式化输出](basic/formatted-output.md)
- [Rust高级进阶](advance/intro.md)
- [生命周期](advance/lifetime/intro.md)
- [认识生命周期](advance/lifetime/basic.md)
- [深入生命周期](advance/lifetime/advance.md)
- [&'static 和 T: 'static](advance/lifetime/static.md)
<!-- - [一些关于生命周期的误解 todo](advance/lifetime/misconceptions.md) -->
- [函数式编程](advance/functional-programing/intro.md)
- [函数式编程: 闭包、迭代器](advance/functional-programing/intro.md)
- [闭包closure](advance/functional-programing/closure.md)
- [迭代器iterator](advance/functional-programing/iterator.md)
- [包和模块](advance/crate-module/intro.md)
- [包crate](advance/crate-module/crate.md)
- [模块Module](advance/crate-module/module.md)
- [使用use引入模块及受限可见性](advance/crate-module/use.md)
- [注释和文档](advance/comment.md)
- [深入类型之newtype和Sized](advance/custom-type.md)
- [格式化输出](advance/formatted-output.md)
- [智能指针](advance/smart-pointer/intro.md)
- [Box<T>堆对象分配](advance/smart-pointer/box.md)
- [Deref解引用](advance/smart-pointer/deref.md)
@ -86,8 +85,12 @@
- [实践应用多线程Web服务器 todo](advance/concurrency-with-threads/web-server.md)
- [全局变量](advance/global-variable.md)
- [错误处理](advance/errors.md)
- [进阶类型转换](advance/converse/intro.md)
- [枚举和整数](advance/converse/enum-int.md)
- [Unsafe Rust](advance/unsafe/intro.md)
- [五种兵器](advance/unsafe/superpowers.md)
- [Macro宏编程](advance/macro.md)
- [SIMD todo](advance/simd.md)
<!-- - [高阶特征约束(HRTB) todo](advance/hrtb.md) -->
## 专题内容,每个专题都配套一个小型项目进行实践
@ -107,7 +110,7 @@
- [一些疑难问题的解决办法](async/pain-points-and-workarounds.md)
- [实践应用Async Web服务器](async/web-server.md)
- [tokio使用指南](tokio/intro.md)
- [Tokio使用指南](tokio/intro.md)
- [tokio概览](tokio/overview.md)
- [使用初印象](tokio/getting-startted.md)
- [创建异步任务](tokio/spawning.md)
@ -121,11 +124,12 @@
- [优雅的关闭](tokio/graceful-shutdown.md)
- [异步跟同步共存](tokio/bridging-with-sync.md)
- [易混淆概念解析 todo](confonding/intro.md)
- [Rust难点解析 todo](confonding/intro.md)
- [String、&str 和 str todo](confonding/string.md)
- [原生指针、引用和智能指针 todo](confonding/pointer.md)
- [作用域、生命周期和 NLL todo](confonding/lifetime.md)
- [move、Copy和Clone todo](confonding/move-copy.md)
- [写时拷贝Cow todo](confonding/cow.md)
- [对抗编译检查 doing](fight-with-compiler/intro.md)
- [幽灵数据(todo)](fight-with-compiler/phantom-data.md)
@ -140,7 +144,7 @@
- [类型未限制(todo)](fight-with-compiler/unconstrained.md)
- [Rust陷阱系列](pitfalls/index.md)
- [Rust常见陷阱](pitfalls/index.md)
- [for循环中使用外部数组](pitfalls/use-vec-in-for.md)
- [线程类型导致的栈溢出](pitfalls/stack-overflow.md)
- [算术溢出导致的panic](pitfalls/arithmetic-overflow.md)
@ -162,18 +166,11 @@
- [代码风格(todo)](practice/style-guide/code.md)
- [代码标记 todo](practice/style-guide/mark.md)
- [Clippy todo](practice/style-guide/clippy.md)
- [日志和监控 todo](practice/logs.md)
- [如何实现一个链表 todo]()
- [高级类型转换](converse/intro.md)
- [枚举和整数](converse/enum-int.md)
<!-- - [复杂错误索引 todo](errorindex/intro.md)
- [所有权和借用 todo](errorindex/borrowing/intro.md)
- [生命周期 todo](errorindex/lifetime/intro.md) -->
- [Cargo详解 todo](cargo/intro.md)
- [Cargo使用指南 todo](cargo/intro.md)
- [常用命令 todo](cargo/commands.md)
- [项目结构 todo](cargo/layout.md)
- [Cargo.toml和Cargo.lock todo](cargo/cargo-toml-lock.md)
@ -186,49 +183,30 @@
- [自定义构建脚本 todo](cargo/build-js.md)
- [Cargo profile todo](cargo/profile.md)
- [常见特征解析 todo](traits/intro.md)
- [类型转换From/Into todo](traits/from-into.md)
- [AsRef, AsMut todo](traits/as-ref-as-mut.md)
- [Borrow, BorrowMut, ToOwned todo](traits/borrow-family.md)
- [Deref和引用隐式转换 todo](traits/deref.md)
- [写时拷贝Cow todo](traits/cow.md)
- [Eq todo](traits/eq.md)
- [深入内存 todo](memory/intro.md)
- [指针和引用 todo](memory/pointer-ref.md)
- [未初始化内存 todo](memory/uninit.md)
- [内存分配 todo](memory/allocation.md)
- [内存布局 todo](memory/layout.md)
- [虚拟内存 todo](memory/virtual.md)
- [宏编程 todo](macro/intro.md)
- [过程宏(todo)](macro/procedure-macro.md)
- [性能调优 doing](performance/intro.md)
- [深入理解move](performance/deep-into-move.md)
- [糟糕的提前优化 todo](performance/early-optimise.md)
- [Clone和Copy todo](performance/clone-copy.md)
- [Benchmark性能测试(todo)](performance/benchmark.md)
- [减少Runtime check(todo)](performance/runtime-check.md)
- [CPU缓存性能优化 todo](performance/cpu-cache.md)
- [计算性能优化 todo](performance/calculate.md)
- [堆和栈 todo](performance/heap-stack.md)
- [内存allocator todo](performance/allocator.md)
- [常用性能测试工具 todo](performance/tools.md)
- [Enum内存优化 todo](performance/enum.md)
- [编译器 todo](compiler/intro.md)
- [常见属性标记 todo](compiler/attributes.md)
- [提升编译速度 todo](compiler/speed-up.md)
- [编译器优化 todo](compiler/optimization/intro.md)
- [Option枚举 todo](compiler/optimization/option.md)
- [日志和监控 todo](monitor/intro.md)
- [日志 todo](monitor/log.md)
- [可观测性 todo](monitor/observability.md)
- [监控(APM) todo](monitor/apm.md)
- [Rust性能剖析 todo](profiling/intro.md)
- [深入内存 todo](profiling/memory/intro.md)
- [指针和引用 todo](profiling/memory/pointer-ref.md)
- [未初始化内存 todo](profiling/memory/uninit.md)
- [内存分配 todo](profiling/memory/allocation.md)
- [内存布局 todo](profiling/memory/layout.md)
- [虚拟内存 todo](profiling/memory/virtual.md)
- [性能调优 doing](profiling/performance/intro.md)
- [深入理解move](profiling/performance/deep-into-move.md)
- [糟糕的提前优化 todo](profiling/performance/early-optimise.md)
- [Clone和Copy todo](profiling/performance/clone-copy.md)
- [Benchmark性能测试(todo)](profiling/profiling/performance/benchmark.md)
- [减少Runtime check(todo)](profiling/performance/runtime-check.md)
- [CPU缓存性能优化 todo](profiling/performance/cpu-cache.md)
- [计算性能优化 todo](profiling/performance/calculate.md)
- [堆和栈 todo](profiling/performance/heap-stack.md)
- [内存allocator todo](profiling/performance/allocator.md)
- [常用性能测试工具 todo](profiling/performance/tools.md)
- [Enum内存优化 todo](profiling/performance/enum.md)
- [编译优化 todo](profiling/compiler/intro.md)
- [常见属性标记 todo](profiling/compiler/attributes.md)
- [提升编译速度 todo](profiling/compiler/speed-up.md)
- [编译器优化 todo](profiling/compiler/optimization/intro.md)
- [Option枚举 todo](profiling/compiler/optimization/option.md)
- [标准库解析 todo](std/intro.md)
- [标准库使用最佳时间 todo](std/search.md)
@ -236,7 +214,13 @@
- [HashMap todo](std/hashmap.md)
- [Iterator常用方法 todo](std/iterator.md)
- [SIMD](simd/intro.md)
- [场景化用例 todo](cases/intro.md)
- [文件操作 todo](cases/file.md)
- [网络通信 todo](cases/protocol.md)
- [JSON处理 todo](cases/json.md)
- [HTTP请求 todo](cases/http.md)
- [数据库访问 todo](cases/database.md)
- [正则表达式 todo](cases/regexp.md)
<!--
- [Rust区块链入门]()
- [Rust游戏开发入门]()

@ -0,0 +1,169 @@
# Macro宏编程
在编程世界可以说是谈“宏”色变,原因在于 C 语言中的宏是非常危险的东东,但并不是所有语言都像 C 这样,例如对于古老的语言 Lisp 来说,宏就是就是一个非常强大的好帮手。
那话说回来,在 Rust 中宏到底是好是坏呢?本章将带你揭开它的神秘面纱。
事实上,我们虽然没有见过宏,但是已经多次用过它,例如在全书的第一个例子中就用到了:`println!("你好,世界")`,这里 `println!` 就是一个最常用的宏,可以看到它和函数最大的区别是:它在调用时多了一个 `!`,除此之外还有 `vec!` 、`assert_eq!` 都是相当常用的,可以说**宏在 Rust 中无处不在**。
细心的读者可能会注意到 `println!` 后面跟着的是 `()`,而 `vec!` 后面跟着的是 `[]`,这是因为宏的参数可以使用 `()`、`[]` 以及 `{}`:
```rust
fn main() {
println!("aaaa");
println!["aaaa"];
println!{"aaaa"}
}
```
虽然三种使用形式皆可,但是 Rust 内置的宏都有自己约定俗成的使用方式,例如 `vec![...]`、`assert_eq!(...)` 等。
在 Rust 中宏分为两大类:声明式宏 `macro_rules!` 和三种过程宏( *procedural macros* ):
- `#[derive]`,在之前多次见到的派生宏,可以为目标结构体或枚举派生指定的代码,例如 `Debug` 特征
- 属性宏(Attribute-like macro),用于为目标添加自定义的属性
- 函数宏(Function-like macro),看上去就像是函数调用
如果感觉难以理解,也不必担心,接下来我们将逐个看看它们的庐山真面目,在次之前,先来看下为何需要宏,特别是 Rust 的函数明明已经很强大了。
## 宏和函数的区别
宏和函数的区别并不少,而且对于宏擅长的领域,函数其实是有些无能为力的。
#### 元编程
从根本上来说,宏是通过一种代码来生成另一种代码,如果大家熟悉元编程,就会发现两者的共同点。
在[附录 D 中](https://course.rs/appendix/derive.html)讲到的 `derive` 属性,就会自动为结构体派生出相应特征所需的代码,例如 `#[derive(Debug)]`,还有熟悉的 `println!``vec!`,所有的这些宏都会展开成相应的代码,且很可能是长得多的代码。
总之,元编程可以帮我们减少所需编写的代码,也可以一定程度上减少维护的成本,虽然函数复用也有类似的作用,但是宏依然拥有自己独特的优势。
#### 可变参数
Rust 的函数签名是固定的:定义了两个参数,就必须传入两个参数,多一个少一个都不行,对于从 JS/TS 过来的同学,这一点其实是有些恼人的。
而宏就可以拥有可变数量的参数,例如可以调用一个参数的 `println!("hello")`,也可以调用两个参数的 `println!("hello {}", name)`
#### 宏展开
由于宏会被展开成其它代码,且这个展开过程是发生在编译器对代码进行解释之前。因此,宏可以为指定的类型实现某个特征:先将宏展开成实现特征的代码后,再被编译。
而函数就做不到这一点,因为它直到运行时才能被调用,而特征需要在编译期被实现。
#### 宏的缺点
相对函数来说,由于宏是基于代码再展开成代码,因此实现相比函数来说会更加复杂,再加上宏的语法更为复杂,最终导致定义宏的代码相当地难读,也难以理解和维护。
## 声明式宏 `macro_rules!`
在 Rust 中使用最广的就是声明式宏,它们也有一些其它的称呼,例如示例宏( macros by example )、`macro_rules!` 或干脆直接称呼为**宏**。
声明式宏允许我们写出类似 `match` 的代码。`match` 表达式是一个控制结构,其接收一个表达式,然后将表达式的结果与多个模式进行匹配,一旦匹配了某个模式,则该模式相关联的代码将被执行:
```rust
match target {
模式1 => 表达式1,
模式2 => {
语句1;
语句2;
表达式2
},
_ => 表达式3
}
```
而**宏也是将一个值跟对应的模式进行匹配,且该模式会与特定的代码相关联**。但是与 `match` 不同的是,**宏里的值是一段 Rust 源代码**(字面量),模式用于跟这段源代码的结构相比较,一旦匹配,传入宏的那段源代码将被模式关联的代码所替换,最终实现宏展开。值得注意的是,**所有的这些都是在编译期发生,并没有运行期的性能损耗**。
#### 简化版的 vec!
在[动态数组 Vector 章节](https://course.rs/basic/collections/vector.html#vec)中,我们学习了使用 `vec!` 来便捷的初始化一个动态数组:
```rust
let v: Vec<u32> = vec![1, 2, 3];
```
最重要的是,通过 `vec!` 创建的动态数组支持任何元素类型,也并没有限制数组的长度,如果使用函数,我们是无法做到这一点的。
好在我们有 `macro_rules!`,来看看该如何使用它来实现 `vec!`,以下是一个简化实现:
```rust
#[macro_export]
macro_rules! vec {
( $( $x:expr ),* ) => {
{
let mut temp_vec = Vec::new();
$(
temp_vec.push($x);
)*
temp_vec
}
};
}
```
简化实现版本?这也太难了吧!!只能说,欢迎来到宏的世界,在这里你能见到优雅 Rust 的另一面:) 标准库中的 `vec!` 还包含了预分配内存空间的代码,如果引入进来,那大家将更难以接受。
`#[macro_export]` 注释将宏进行了导出,这样其它的包就可以将该宏引入到当前作用域中,然后才能使用。可能有同学会提问:我们在使用标准库 `vec!` 时也没有引入宏啊,那是因为 Rust 已经通过 [`std::prelude`](https://course.rs/appendix/prelude.html) 的方式为我们自动引入了。
紧接着,就使用 `macro_rules!` 进行了宏定义,需要注意的是宏的名称是 `vec`,而不是 `vec!`,后者的感叹号只在调用时才需要。
`vec` 的定义结构跟 `match` 表达式很像,但这里我们只有一个分支,其中包含一个模式 `( $( $x:expr ),* )`,跟模式相关联的代码就在 `=>` 之后。一旦模式成功匹配,那这段相关联的代码就会替换传入的源代码。
由于 `vec` 宏只有一个模式,因此它只能匹配一种源代码,其它类型的都将导致报错,而更复杂的宏往往会拥有更多的分支。
虽然宏和 `match` 都称之为模式,但是前者跟[后者](https://course.rs/basic/match-pattern/all-patterns.html)的模式规则是不同的。如果大家想要更深入的了解宏的模式,可以查看[这里](https://doc.rust-lang.org/reference/macros-by-example.html)。
#### 模式解析
而现在,我们先来简单讲解下 `( $( $x:expr ),* )` 的含义。
首先,我们使用圆括号 `()` 将整个宏模式包裹其中。紧随其后的是 `$()`,跟括号中模式相匹配的值(传入的 Rust 源代码)会被捕获,然后用于代码替换。在这里,模式 `$x:expr` 会匹配任何 Rust 表达式并给予该模式一个名称:`$x`。
`$()` 之后的逗号说明在 `$()` 所匹配的代码的后面会有一个可选的逗号分隔符,紧随逗号之后的 `*` 说明 `*` 之前的模式会被匹配一次或任意多次(类似正则表达式)。
当我们使用 `vec![1, 2, 3]` 来调用该宏时,`$x` 模式将被匹配三次,分别是 `1`、`2`、`3`。为了帮助大家巩固,我们再来一起过一下:
1. `$()` 中包含的是模式 `$x:expr`,该模式中的 `expr` 表示会匹配任何 Rust 表达式,并给予该模式一个名称 `$x`
2. 因此 `$x` 模式可以跟整数 `1` 进行匹配,也可以跟字符串 "hello" 进行匹配: `vec!["hello", "world"]`
3. `$()` 之后的逗号,意味着`1` 和 `2` 之间可以使用逗号进行分割,也意味着 `3` 既可以没有逗号,也可以有逗号:`vec![1, 2, 3,]`
4. `*` 说明之前的模式可以出现一次也可以任意次,这里出现了三次
接下来,我们再来看看与模式相关联、在 `=>` 之后的代码:
```rust
{
{
let mut temp_vec = Vec::new();
$(
temp_vec.push($x);
)*
temp_vec
}
};
```
这里就比较好理解了,`$()` 中的 `temp_vec.push()` 将根据模式匹配的次数生成对应的代码,当调用 `vec![1, 2, 3]` 时,下面这段生成的代码将替代传入的源代码,也就是替代 `vec![1, 2, 3]` :
```rust
{
let mut temp_vec = Vec::new();
temp_vec.push(1);
temp_vec.push(2);
temp_vec.push(3);
temp_vec
}
```
如果是 `let v = vec![1, 2, 3]`,那生成的代码最后返回的值 `temp_vec` 将被赋予给变量 `v`,等同于 :
```rust
let v = {
let mut temp_vec = Vec::new();
temp_vec.push(1);
temp_vec.push(2);
temp_vec.push(3);
temp_vec
}
```
至此,我们定义了一个宏,它可以接受任意类型和数量的参数,并且理解了其语法的含义。
#### 未来将被替代的 `macro_rules`
对于 `macro_rules` 来说它是存在一些问题的因此Rust 计划在未来使用新的声明式宏来替换它:工作方式类似,但是解决了目前存在的一些问题,在那之后,`macro_rules` 将变为 `deprecated` 状态。
由于绝大多数 Rust 开发者都是宏的用户而不是编写者,因此在这里我们不会对 `macro_rules` 进行更深入的学习,如果大家感兴趣,可以看看这本书 [ “The Little Book of Rust Macros”](https://veykril.github.io/tlborm/)。
## 额外的学习资料
https://www.reddit.com/r/rust/comments/s3mm8m/macro_hygiene/
https://www.reddit.com/r/rust/comments/rjumsg/any_good_resources_for_learning_rust_macros/
https://www.reddit.com/r/rust/comments/roaofg/procedural_macros_parsing_custom_syntax/

@ -0,0 +1 @@
# 数据库访问 todo

@ -0,0 +1 @@
# 文件操作

@ -0,0 +1 @@
# HTTP请求 todo

@ -0,0 +1 @@
# 场景化用例

@ -0,0 +1 @@
# JSON处理 todo

@ -0,0 +1 @@
# 网络通信 todo

@ -0,0 +1 @@
# 正则表达式 todo

@ -0,0 +1 @@
# 写时拷贝Cow todo

@ -1,3 +0,0 @@
# macro
https://www.reddit.com/r/rust/comments/s3mm8m/macro_hygiene/

@ -1,4 +0,0 @@
https://www.reddit.com/r/rust/comments/rjumsg/any_good_resources_for_learning_rust_macros/
https://www.reddit.com/r/rust/comments/roaofg/procedural_macros_parsing_custom_syntax/

@ -1 +0,0 @@
# 监控(APM)

@ -1 +0,0 @@
# 日志和监控

@ -1,4 +1,4 @@
# 日志
# 日志和监控 todo
```rust

@ -0,0 +1 @@
# Rust性能剖析 todo

@ -1 +0,0 @@
# Borrow, BorrowMut, ToOwned

@ -1 +0,0 @@
# 写时拷贝Cow

@ -1,89 +0,0 @@
# Deref和引用隐式转换
`Deref` 是解引用操作符`*`的特征,比如 *v。
一般理解,`*v`操作,是`&v`的反向操作,是为了获取`&v`指针指向的堆上对象。
## 强制隐式转换
Deref最神奇、最好用的地方并不在本身`解引`这个意义上Rust的设计者在它之上附加了一个特性强制隐式转换这才是它神奇之处。
这种隐式转换的规则为:
一个类型为`T`的对象`foo`如果T: Deref<Target=U>,那么,相关`foo`的引用`&foo`在应用的时候会自动转换`&U`。
粗看这条规则,貌似有点类似于`AsRef`,而跟`解引`似乎风马牛不相及, 实际里面里面有些玄妙之处。
Rust编译器会在做`*v`操作的时候,自动先把`v`做引用归一化操作,即转换成内部通用引用的形式`&v`,整个表达式就变成 `*&v`。这里面有两种情况:
1. 把智能指针比如在库中定义的Box, Rc, Arc, Cow 等),去掉壳,转成内部标准形式`&v`
2. 把多重`&` (比如:`&&&&&&&v`),简化成`&v`(通过插入足够数量的`*`进行解引)。
所以,它实际上在解引用之前做了一个引用的归一化操作。
为什么要转呢? 因为编译器设计的能力是,只能够对 &v 这种引用进行解引用。其它形式的它不认识,所以要做引用归一化操作。
使用引用进行过渡也是为了能够防止不必要的拷贝。
下面举一些例子:
```rust
fn foo(s: &str) {
// borrow a string for a second
}
// String implements Deref<Target=str>
let owned = "Hello".to_string();
// therefore, this works:
foo(&owned);
```
因为`String`实现了`Deref<Target=str>`。
```rust
use std::rc::Rc;
fn foo(s: &str) {
// borrow a string for a second
}
// String implements Deref<Target=str>
let owned = "Hello".to_string();
let counted = Rc::new(owned);
// therefore, this works:
foo(&counted);
```
因为`Rc<T>`实现了`Deref<Target=T>`。
```rust
fn foo(s: &[i32]) {
// borrow a slice for a second
}
// Vec<T> implements Deref<Target=[T]>
let owned = vec![1, 2, 3];
foo(&owned);
```
因为`Vec<T>` 实现了`Deref<Target=[T]>`。
```rust
struct Foo;
impl Foo {
fn foo(&self) { println!("Foo"); }
}
let f = &&Foo;
f.foo();
(&f).foo();
(&&f).foo();
(&&&&&&&&f).foo();
```
上面那几种函数的调用,效果是一样的。
这种`Deref`涉及的隐式转换实际上是Rust中仅有的类型隐式转换设计它的目的是为了简化程序的书写让代码不至于过于繁琐。把人从无尽的类型细节中解脱出来让书写 Rust 代码变成一件快乐的事情。

@ -1,26 +0,0 @@
# Eq
The rules for == and friends in Rust are a bit complicated. Consider some type T that implements Eq:
- You can always compare objects of type T. The comparison will be notionally done as if by reference, meaning there will be no move or copy involved.
- You can always compare references to objects of type T. The objects must match in "reference depth": &&a == &&a is OK, &a == &&a is not. Dereferences count here: &*&&a == &&*&a is OK.
- Some types — for example, String — have a Deref implementation. This will play a role in comparisons because they are "auto-derefed". For example, if s is a String then s == "x" is OK, because s will automatically be treated as &str for comparison purposes.
In general, you can't dereference a string literal. A string literal is of type `&'static str`. If you dereference it you get a str, which is unsized and thus really hard to work with.
However, `==` and friends are special. From the Rust Reference Manual:
> Unlike the arithmetic and logical operators above, these operators implicitly take shared borrows of their operands, evaluating them in place expression context:
```rust
a == b;
// is equivalent to
::std::cmp::PartialEq::eq(&a, &b);
```
> This means that the operands don't have to be moved out of.
So when you write `*"s" == *"t"` it is treated as
```rust
::std::cmp::PartialEq::eq(&*"s", &*"t");
```
and thus works even though it looks like it shouldn't.

@ -1 +0,0 @@
# 类型转换From/Into

@ -1 +0,0 @@
# 常见特征解析
Loading…
Cancel
Save