From a6f2d414c363fef3149b654f6bb70af39b1ec9c7 Mon Sep 17 00:00:00 2001 From: lijinpeng Date: Sat, 22 Jan 2022 14:17:04 +0800 Subject: [PATCH 1/6] Minor update in box.md --- book/contents/advance/smart-pointer/box.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/contents/advance/smart-pointer/box.md b/book/contents/advance/smart-pointer/box.md index 590ee843..823176a6 100644 --- a/book/contents/advance/smart-pointer/box.md +++ b/book/contents/advance/smart-pointer/box.md @@ -6,7 +6,7 @@ ## Rust中的堆栈 高级语言Python/Java等往往会弱化堆栈的概念,但是要用好C/C++/Rust,就必须对堆栈有深入的了解,原因是两者的内存管理方式不同: 前者有GC垃圾回收机制, 因此无需你去关心内存的细节。 -栈内存从高位地址向下增长,且栈内存是连续分配的,一般来说**操作系统对栈内存的大小都有限制**,因此C语言中无法创建任意长度的数组。在Rust中, `main`线程的[栈大小是`8MB`](https://zhuanlan.zhihu.com/p/446039229),普通线程是`2MB`,在函数调用时会在其中创建一个临时栈空间,调用结束后Rust会让这个栈空间里的对象自动进入`Drop`流程,最后栈顶指针自动移动到上一个调用栈顶,无需程序员手动干预,因而栈内存申请和释放是非常高效的。 +栈内存从高位地址向下增长,且栈内存是连续分配的,一般来说**操作系统对栈内存的大小都有限制**,因此C语言中无法创建任意长度的数组。在Rust中, `main`线程的[栈大小是`8MB`](https://course.rs/pitfalls/stack-overflow.html),普通线程是`2MB`,在函数调用时会在其中创建一个临时栈空间,调用结束后Rust会让这个栈空间里的对象自动进入`Drop`流程,最后栈顶指针自动移动到上一个调用栈顶,无需程序员手动干预,因而栈内存申请和释放是非常高效的。 与栈相反,堆上内存则是从低位地址向上增长,**堆内存通常只受物理内存限制**,而且通常是不连续的, 因此从性能的角度看,栈往往比对堆更高。 @@ -56,7 +56,7 @@ fn main() { } ``` -这样就可以创建一个智能指针指向了存储在堆上的`5`,并且`a`持有了该指针。在本章的引言中,我们提到了智能指针往往都实现了`Deref`和`Drop`特征,因此: +这样就可以创建一个智能指针指向了存储在堆上的`3`,并且`a`持有了该指针。在本章的引言中,我们提到了智能指针往往都实现了`Deref`和`Drop`特征,因此: - `println!`可以正常打印出`a`的值,是因为它隐式的调用了`Deref`对智能指针`a`进行了解引用 - 最后一行代码`let b = a + 1`报错,是因为在表达式中,我们无法自动隐式的执行`Deref`解引用操作, 你需要使用`*`操作符`let b = *a + 1`,来显式的进行解引用 From 0f56ae4480d4b8fdd4c820e8d4e9c8d35da5a498 Mon Sep 17 00:00:00 2001 From: lijinpeng Date: Sat, 22 Jan 2022 14:50:48 +0800 Subject: [PATCH 2/6] Fix typo in deref.md --- book/contents/advance/smart-pointer/deref.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/book/contents/advance/smart-pointer/deref.md b/book/contents/advance/smart-pointer/deref.md index fd842767..47b07626 100644 --- a/book/contents/advance/smart-pointer/deref.md +++ b/book/contents/advance/smart-pointer/deref.md @@ -149,7 +149,7 @@ fn display(s: &str) { ```rust fn main() { let m = MyBox::new(String::from("Rust")); - hello(&(*m)[..]); + display(&(*m)[..]); } ``` @@ -161,7 +161,7 @@ fn main() { ```rust fn main() { let s = MyBox::new(String::from("hello, world")); - let s1:&str = &s; + let s1: &str = &s; let s2: String = s.to_string(); } ``` @@ -171,14 +171,14 @@ fn main() { ## Deref规则总结 在上面,我们零碎的介绍了不少关于`Deref`特征的知识,下面来通过较为正式的方式来对其规则进行下总结。 -一个类型为`T`的对象`foo`,如果`T: Deref`,那么,相关`foo`的引用`&foo`在应用的时候会自动转换`&U`。 +一个类型为`T`的对象`foo`,如果`T: Deref`,那么,相关`foo`的引用`&foo`在应用的时候会自动转换为`&U`。 -粗看这条规则,貌似有点类似于`AsRef`,而跟`解引`似乎风马牛不相及, 实际里面里面有些玄妙之处。 +粗看这条规则,貌似有点类似于`AsRef`,而跟`解引用`似乎风马牛不相及, 实际里面有些玄妙之处。 Rust编译器会在做`*v`操作的时候,自动先把`v`做引用归一化操作,即转换成内部通用引用的形式`&v`,整个表达式就变成 `*&v`。这里面有两种情况: 1. 把智能指针(比如在库中定义的,Box, Rc, Arc, Cow 等),去掉壳,转成内部标准形式`&v`; -2. 把多重`&` (比如:`&&&&&&&v`),简化成`&v`(通过插入足够数量的`*`进行解引)。 +2. 把多重`&` (比如:`&&&&&&&v`),简化成`&v`(通过插入足够数量的`*`进行解引用)。 所以,它实际上在解引用之前做了一个引用的归一化操作。 为什么要转呢? 因为编译器设计的能力是,只能够对 &v 这种引用进行解引用。其它形式的它不认识,所以要做引用归一化操作。 @@ -215,7 +215,7 @@ Rust编译器会在做`*v`操作的时候,自动先把`v`做引用归一化操 foo(&counted); ``` -因为`Vec` 实现了`Deref`。 +因为`Rc` 实现了`Deref`。 ```rust struct Foo; @@ -283,7 +283,7 @@ fn display(s: &mut String) { 以上代码有几点值得注意: -- 要实现`DerefMut`必须要先实现`Deref`特征: `pub trait DerefMut: Deref {` +- 要实现`DerefMut`必须要先实现`Deref`特征: `pub trait DerefMut: Deref` - `T: DerefMut`解读:将`&mut T`类型通过`DerefMut`特征的方法转换为`&mut U`类型,对应上例中,就是将`&mut MyBox`转换为`&mut String` 对于上述三条规则中的第三条,它比另外两条稍微复杂了点:Rust可以把可变引用隐式的转换成不可变引用,但反之则不行。 From 7aaf6c9479657807f82607d80c0fa6c2e6beaea9 Mon Sep 17 00:00:00 2001 From: lijinpeng Date: Sat, 22 Jan 2022 15:14:15 +0800 Subject: [PATCH 3/6] Fix typo in rc-arc.md --- book/contents/advance/smart-pointer/drop.md | 6 +++--- book/contents/advance/smart-pointer/rc-arc.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/book/contents/advance/smart-pointer/drop.md b/book/contents/advance/smart-pointer/drop.md index d731a460..27dcd35c 100644 --- a/book/contents/advance/smart-pointer/drop.md +++ b/book/contents/advance/smart-pointer/drop.md @@ -7,7 +7,7 @@ ## Rust中的资源回收 在一些无GC语言中,程序员在一个变量无需再被使用时,需要手动释放它占用的内存资源,如果忘记了,那么就会发生内存泄漏,最终臭名昭著的`OOM`问题可能就会发生。 -而在Rust中,你可以指定在一个变量超出作用域时,执行一段特定的代码,最终编译器将帮你自动插入这段收尾代码。这样,就无需在每一个使用该变量的地方,都写一段代码来进行收尾工作和资源释放。不仅让人感叹,Rust的大腿真粗,香! +而在Rust中,你可以指定在一个变量超出作用域时,执行一段特定的代码,最终编译器将帮你自动插入这段收尾代码。这样,就无需在每一个使用该变量的地方,都写一段代码来进行收尾工作和资源释放。不禁让人感叹,Rust的大腿真粗,香! 没错,指定这样一段收尾工作靠的就是咱这章的主角 - `Drop`特征。 @@ -70,7 +70,7 @@ Dropping HasDrop2! #### Drop的顺序 观察以上输出,我们可以得出以下关于`Drop`顺序的结论 -- **变量级别,按照逆序的方式**,,`_x`在`_foo`之前创建,因此`_x`在`_foo`之后被drop +- **变量级别,按照逆序的方式**,`_x`在`_foo`之前创建,因此`_x`在`_foo`之后被drop - **结构体内部,按照顺序的方式**, 结构体`_x`中的字段按照定义中的顺序依次`drop` #### 没有实现Drop的结构体 @@ -152,7 +152,7 @@ Bingo,完美拿走了所有权,而且这种实现保证了后续的使用必 对于第二点,在之前我们已经详细介绍过,因此这里主要对第一点进行下简单说明。 -在绝大多数情况下,我们都无需手动去`drop`以回收内存资源,因为Rust会自动帮我们完成这些工作,它甚至会对复杂类型的每个字段都单独的调用`drop`进行回收!但是确实有极少数情况,需要你自己来回收资源的,例如文件描述符、网络socket等,当这些超出作用域不再使用时,就需要进行关闭以释放相关的资源,在这些情况下,就需要使用者自己来解决`Drop`的问题。 +在绝大多数情况下,我们都无需手动去`drop`以回收内存资源,因为Rust会自动帮我们完成这些工作,它甚至会对复杂类型的每个字段都单独的调用`drop`进行回收!但是确实有极少数情况,需要你自己来回收资源的,例如文件描述符、网络socket等,当这些值超出作用域不再使用时,就需要进行关闭以释放相关的资源,在这些情况下,就需要使用者自己来解决`Drop`的问题。 ## 互斥的Copy和Drop diff --git a/book/contents/advance/smart-pointer/rc-arc.md b/book/contents/advance/smart-pointer/rc-arc.md index d824c47f..8f8d4344 100644 --- a/book/contents/advance/smart-pointer/rc-arc.md +++ b/book/contents/advance/smart-pointer/rc-arc.md @@ -47,7 +47,7 @@ fn main() { 不要给`clone`字样所迷惑,以为所有的`clone`都是深拷贝。这里的`clone`**仅仅复制了智能指针并增加了引用计数,并没有克隆底层数据**,因此`a`和`b`是共享了底层的字符串`s`,这种**复制效率是非常高**的。当然你也可以使用`a.clone()`的方式来克隆,但是从可读性角度,`Rc::clone`的方式我们更加推荐。 -实际上Rust中,还有不少`clone`都是浅拷贝,例如[迭代器的克隆](https://zhuanlan.zhihu.com/p/453149727). +实际上Rust中,还有不少`clone`都是浅拷贝,例如[迭代器的克隆](https://course.rs/pitfalls/iterator-everywhere.html). #### 观察引用计数的变化 使用关联函数`Rc::strong_count`可以获取当前引用计数的值,我们来观察下引用计数如何随着变量声明、释放而变化: From e0c78cb27f58997af97d35ff139ef8664937ab64 Mon Sep 17 00:00:00 2001 From: lijinpeng Date: Sat, 22 Jan 2022 15:58:01 +0800 Subject: [PATCH 4/6] Fix typo in cell-refcell.md --- book/contents/advance/smart-pointer/cell-refcell.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/book/contents/advance/smart-pointer/cell-refcell.md b/book/contents/advance/smart-pointer/cell-refcell.md index c611fbc6..b71dcc84 100644 --- a/book/contents/advance/smart-pointer/cell-refcell.md +++ b/book/contents/advance/smart-pointer/cell-refcell.md @@ -113,7 +113,7 @@ let z = &mut x; x = 2; *y = 3; *z = 4; -println!("{}", x; +println!("{}", x); ``` 虽然性能一致,但代码`1`拥有代码`2`不具有的优势:它能编译成功:) @@ -204,7 +204,7 @@ fn main() { let s1 = s.clone(); let s2 = s.clone(); - // let mut s2 = .borrow_mut(); + // let mut s2 = s.borrow_mut(); s2.borrow_mut().push_str(", on yeah!"); println!("{:?}\n{:?}\n{:?}", s, s1, s2); @@ -339,9 +339,9 @@ fn retain_even(nums: &mut Vec) { } ``` -此时代码将不会报错,因为`Cell`上的`set`方法获取的是不可变引用`pub fn set(&self, val: T) {`. +此时代码将不会报错,因为`Cell`上的`set`方法获取的是不可变引用`pub fn set(&self, val: T)`. -当然,以上代码的本质还是对`Cell`的运用,只不过这两个方法可以很方便的帮我们把`&mut T`类型转换成`&[Cell]`类型。 +当然,以上代码的本质还是对`Cell`的运用,只不过这两个方法可以很方便的帮我们把`&mut [T]`类型转换成`&[Cell]`类型。 ## 总结 From bf2e217404639b510ad94139770c262b0b634401 Mon Sep 17 00:00:00 2001 From: lijinpeng Date: Sat, 22 Jan 2022 16:24:25 +0800 Subject: [PATCH 5/6] Fix typo in circle-reference.md --- book/contents/advance/circle-self-ref/circle-reference.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/book/contents/advance/circle-self-ref/circle-reference.md b/book/contents/advance/circle-self-ref/circle-reference.md index e526b6c8..f2b769f6 100644 --- a/book/contents/advance/circle-self-ref/circle-reference.md +++ b/book/contents/advance/circle-self-ref/circle-reference.md @@ -91,7 +91,7 @@ thread 'main' has overflowed its stack fatal runtime error: stack overflow ``` -通过`a.tail`的调用,Rust试图打印出`a -> b ->a···`的所有内容,但是在不懈的努力后,`main`线程终于不堪重负,发生了[栈溢出](https://zhuanlan.zhihu.com/p/446039229)。 +通过`a.tail`的调用,Rust试图打印出`a -> b ->a···`的所有内容,但是在不懈的努力后,`main`线程终于不堪重负,发生了[栈溢出](https://course.rs/pitfalls/stack-overflow.html)。 以上的代码可能并不会造成什么大的问题,但是在一个更加复杂的程序中,类似的问题可能会造成你的程序不断的分配内存、泄漏内存,最终程序会不幸**OOM**,当然这其中的CPU损耗也不可小觑。 @@ -279,9 +279,9 @@ fn main() { 这个例子就留给读者自己解读和分析,我们就不画蛇添足了:) ## unsafe解决循环引用 -除了使用Rust标准库提供的这些类型,你还可以使用`unsafe`里的原生指针来解决这些棘手的问题,但是由于我们还没有讲解`unsafe`,因此这里就不进行展开,只附上[源码链接](https://codes.rs/unsafe/self-ref.html), 挺长的,需要耐心o,O +除了使用Rust标准库提供的这些类型,你还可以使用`unsafe`里的原生指针来解决这些棘手的问题,但是由于我们还没有讲解`unsafe`,因此这里就不进行展开,只附上[源码链接](https://codes.rs/unsafe/self-ref.html), 挺长的,需要耐心o_O -虽然`unsfae`不安全,但是在各种库的代码中依然很常见用它来实现自引用结构,主要优点如下: +虽然`unsafe`不安全,但是在各种库的代码中依然很常见用它来实现自引用结构,主要优点如下: - 性能高,毕竟直接用原生指针操作 - 代码更简单更符合直觉: 对比下`Option>>` From 5b5a9ed4c7584dad1637a338a1ce3715e361f4dd Mon Sep 17 00:00:00 2001 From: lijinpeng Date: Sat, 22 Jan 2022 16:57:47 +0800 Subject: [PATCH 6/6] Minor update in self-referential.md --- .../advance/circle-self-ref/self-referential.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/book/contents/advance/circle-self-ref/self-referential.md b/book/contents/advance/circle-self-ref/self-referential.md index 4754a8f8..b0db13ad 100644 --- a/book/contents/advance/circle-self-ref/self-referential.md +++ b/book/contents/advance/circle-self-ref/self-referential.md @@ -196,7 +196,7 @@ fn main() { 运行后输出: ```console hello, 0x16f3aec70 -hello, world!, 0x16f3#aec70 +hello, world!, 0x16f3aec70 ``` 上面的`unsafe`虽然简单好用,但是它不太安全,是否还有其他选择?还真的有,那就是`Pin`。 @@ -254,12 +254,12 @@ fn main() { 上面的代码也非常清晰,虽然使用了`unsafe`,其实更多的是无奈之举,跟之前的`unsafe`实现完全不可同日而语。 -其实`Pin`在这里并没有魔法,它也并不是实现自引用类型的主要原因,最关键的还是里面的原生指针的使用,而`Pin`起到的就是确保我们的值不会被移走,否则指针就会指向一个错误的地址! +其实`Pin`在这里并没有魔法,它也并不是实现自引用类型的主要原因,最关键的还是里面的原生指针的使用,而`Pin`起到的作用就是确保我们的值不会被移走,否则指针就会指向一个错误的地址! ## 使用ouroboros -对于自引用结构体,三方库也有支持的,其中一个就是`ouroboros`,当然它也有自己的限制,我们后面会提到,先来看看该如何使用: +对于自引用结构体,三方库也有支持的,其中一个就是[ouroboros](https://github.com/joshua-maros/ouroboros),当然它也有自己的限制,我们后面会提到,先来看看该如何使用: ```rust use ouroboros::self_referencing; @@ -337,7 +337,7 @@ fn main() { 类似的库还有: - [rental](https://github.com/jpernst/rental), 这个库其实是最有名的,但是好像不再维护了,用倒是没问题 -- [owning-ref](https://github.com/Kimundi/owning-ref-rs) +- [owning-ref](https://github.com/Kimundi/owning-ref-rs) ,将所有者和它的引用绑定到一个封装类型 这三个库,各有各的特点,也各有各的缺陷,建议大家需要时,一定要仔细调研,并且写demo进行测试,不可大意。 @@ -352,7 +352,7 @@ fn main() { ## 学习一本书:如何实现链表 -最后,推荐一本专门将如何实现链表的书(真是富有Rust特色,链表都能复杂到出书了O, O),[too many lists](https://rust-unofficial.github.io/too-many-lists/) +最后,推荐一本专门将如何实现链表的书(真是富有Rust特色,链表都能复杂到出书了O, O),[Learn Rust by writing Entirely Too Many Linked Lists](https://rust-unofficial.github.io/too-many-lists/) ## 总结