diff --git a/assets/Rust中英翻译对照表.md b/assets/Rust中英翻译对照表.md index 111273f2..6c825129 100644 --- a/assets/Rust中英翻译对照表.md +++ b/assets/Rust中英翻译对照表.md @@ -301,7 +301,7 @@ RAII | 资源获取即初始化(一般不译) | range | 区间,范围 | range expression | 区间表达式 | raw identifier | 原生标识符 | -raw pointer | 原生指针,裸指针 | +raw pointer | 裸指针 | RC | 引用计数 | reference counted reader | 读取器 | reader/writer | 读写器 | diff --git a/src/SUMMARY.md b/src/SUMMARY.md index fb9edd37..deb8668a 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -101,7 +101,7 @@ - [切片和切片引用](advance/confonding/slice.md) - [Eq 和 PartialEq](advance/confonding/eq.md) - [String、&str 和 str todo](advance/confonding/string.md) - - [原生指针、引用和智能指针 todo](advance/confonding/pointer.md) + - [裸指针、引用和智能指针 todo](advance/confonding/pointer.md) - [作用域、生命周期和 NLL todo](advance/confonding/lifetime.md) - [move、Copy 和 Clone todo](advance/confonding/move-copy.md) @@ -215,7 +215,7 @@ - [最终代码](too-many-lists/deque/final-code.md) - [不错的unsafe队列](too-many-lists/unsafe-queue/intro.md) - [数据布局](too-many-lists/unsafe-queue/layout.md) - + - [基本操作](too-many-lists/unsafe-queue/basics.md) - [Rust 性能优化 todo](profiling/intro.md) - [深入内存 todo](profiling/memory/intro.md) diff --git a/src/advance/circle-self-ref/circle-reference.md b/src/advance/circle-self-ref/circle-reference.md index 6839f544..197c87ab 100644 --- a/src/advance/circle-self-ref/circle-reference.md +++ b/src/advance/circle-self-ref/circle-reference.md @@ -295,11 +295,11 @@ fn main() { ## unsafe 解决循环引用 -除了使用 Rust 标准库提供的这些类型,你还可以使用 `unsafe` 里的原生指针来解决这些棘手的问题,但是由于我们还没有讲解 `unsafe`,因此这里就不进行展开,只附上[源码链接](https://github.com/sunface/rust-algos/blob/fbcdccf3e8178a9039329562c0de0fd01a3372fb/src/unsafe/self-ref.md), 挺长的,需要耐心 o_o +除了使用 Rust 标准库提供的这些类型,你还可以使用 `unsafe` 里的裸指针来解决这些棘手的问题,但是由于我们还没有讲解 `unsafe`,因此这里就不进行展开,只附上[源码链接](https://github.com/sunface/rust-algos/blob/fbcdccf3e8178a9039329562c0de0fd01a3372fb/src/unsafe/self-ref.md), 挺长的,需要耐心 o_o 虽然 `unsafe` 不安全,但是在各种库的代码中依然很常见用它来实现自引用结构,主要优点如下: -- 性能高,毕竟直接用原生指针操作 +- 性能高,毕竟直接用裸指针操作 - 代码更简单更符合直觉: 对比下 `Option>>` ## 总结 diff --git a/src/advance/circle-self-ref/self-referential.md b/src/advance/circle-self-ref/self-referential.md index 4daf9d08..11fedf63 100644 --- a/src/advance/circle-self-ref/self-referential.md +++ b/src/advance/circle-self-ref/self-referential.md @@ -160,9 +160,9 @@ fn main() { } ``` -在这里,我们在 `pointer_to_value` 中直接存储原生指针,而不是 Rust 的引用,因此不再受到 Rust 借用规则和生命周期的限制,而且实现起来非常清晰、简洁。但是缺点就是,通过指针获取值时需要使用 `unsafe` 代码。 +在这里,我们在 `pointer_to_value` 中直接存储裸指针,而不是 Rust 的引用,因此不再受到 Rust 借用规则和生命周期的限制,而且实现起来非常清晰、简洁。但是缺点就是,通过指针获取值时需要使用 `unsafe` 代码。 -当然,上面的代码你还能通过原生指针来修改 `String`,但是需要将 `*const` 修改为 `*mut`: +当然,上面的代码你还能通过裸指针来修改 `String`,但是需要将 `*const` 修改为 `*mut`: ```rust #[derive(Debug)] @@ -230,7 +230,7 @@ use std::ptr::NonNull; // 下面是一个自引用数据结构体,因为 slice 字段是一个指针,指向了 data 字段 // 我们无法使用普通引用来实现,因为违背了 Rust 的编译规则 -// 因此,这里我们使用了一个原生指针,通过 NonNull 来确保它不会为 null +// 因此,这里我们使用了一个裸指针,通过 NonNull 来确保它不会为 null struct Unmovable { data: String, slice: NonNull, @@ -272,7 +272,7 @@ fn main() { 上面的代码也非常清晰,虽然使用了 `unsafe`,其实更多的是无奈之举,跟之前的 `unsafe` 实现完全不可同日而语。 -其实 `Pin` 在这里并没有魔法,它也并不是实现自引用类型的主要原因,最关键的还是里面的原生指针的使用,而 `Pin` 起到的作用就是确保我们的值不会被移走,否则指针就会指向一个错误的地址! +其实 `Pin` 在这里并没有魔法,它也并不是实现自引用类型的主要原因,最关键的还是里面的裸指针的使用,而 `Pin` 起到的作用就是确保我们的值不会被移走,否则指针就会指向一个错误的地址! ## 使用 ouroboros diff --git a/src/advance/concurrency-with-threads/send-sync.md b/src/advance/concurrency-with-threads/send-sync.md index 0301b2b3..1caf4bcc 100644 --- a/src/advance/concurrency-with-threads/send-sync.md +++ b/src/advance/concurrency-with-threads/send-sync.md @@ -1,6 +1,6 @@ # 基于 Send 和 Sync 的线程安全 -为何 Rc、RefCell 和原生指针不可以在多线程间使用?如何让原生指针可以在多线程使用?我们一起来探寻下这些问题的答案。 +为何 Rc、RefCell 和裸指针不可以在多线程间使用?如何让裸指针可以在多线程使用?我们一起来探寻下这些问题的答案。 ## 无法用于多线程的`Rc` @@ -80,7 +80,7 @@ unsafe impl Sync for Mutex {} 正是因为以上规则,Rust 中绝大多数类型都实现了`Send`和`Sync`,除了以下几个(事实上不止这几个,只不过它们比较常见): -- 原生指针两者都没实现,因为它本身就没有任何安全保证 +- 裸指针两者都没实现,因为它本身就没有任何安全保证 - `UnsafeCell`不是`Sync`,因此`Cell`和`RefCell`也不是 - `Rc`两者都没实现(因为内部的引用计数器不是线程安全的) @@ -88,11 +88,11 @@ unsafe impl Sync for Mutex {} **手动实现 `Send` 和 `Sync` 是不安全的**,通常并不需要手动实现 Send 和 Sync trait,实现者需要使用`unsafe`小心维护并发安全保证。 -至此,相关的概念大家已经掌握,但是我敢肯定,对于这两个滑不溜秋的家伙,大家依然会非常模糊,不知道它们该如何使用。那么我们来一起看看如何让原生指针可以在线程间安全的使用。 +至此,相关的概念大家已经掌握,但是我敢肯定,对于这两个滑不溜秋的家伙,大家依然会非常模糊,不知道它们该如何使用。那么我们来一起看看如何让裸指针可以在线程间安全的使用。 -## 为原生指针实现`Send` +## 为裸指针实现`Send` -上面我们提到原生指针既没实现`Send`,意味着下面代码会报错: +上面我们提到裸指针既没实现`Send`,意味着下面代码会报错: ```rust use std::thread; @@ -128,7 +128,7 @@ fn main() { 此时,我们的指针已经可以欢快的在多线程间撒欢,以上代码很简单,但有一点需要注意:`Send`和`Sync`是`unsafe`特征,实现时需要用`unsafe`代码块包裹。 -## 为原生指针实现`Sync` +## 为裸指针实现`Sync` 由于`Sync`是多线程间共享一个值,大家可能会想这么实现: @@ -188,9 +188,9 @@ unsafe impl Sync for MyBox {} ## 总结 -通过上面的两个原生指针的例子,我们了解了如何实现`Send`和`Sync`,以及如何只实现`Send`而不实现`Sync`,简单总结下: +通过上面的两个裸指针的例子,我们了解了如何实现`Send`和`Sync`,以及如何只实现`Send`而不实现`Sync`,简单总结下: 1. 实现`Send`的类型可以在线程间安全的传递其所有权, 实现`Sync`的类型可以在线程间安全的共享(通过引用) -2. 绝大部分类型都实现了`Send`和`Sync`,常见的未实现的有:原生指针、`Cell`、`RefCell`、`Rc` 等 +2. 绝大部分类型都实现了`Send`和`Sync`,常见的未实现的有:裸指针、`Cell`、`RefCell`、`Rc` 等 3. 可以为自定义类型实现`Send`和`Sync`,但是需要`unsafe`代码块 -4. 可以为部分 Rust 中的类型实现`Send`、`Sync`,但是需要使用`newtype`,例如文中的原生指针例子 +4. 可以为部分 Rust 中的类型实现`Send`、`Sync`,但是需要使用`newtype`,例如文中的裸指针例子 diff --git a/src/advance/confonding/pointer.md b/src/advance/confonding/pointer.md index 146132f8..e07a9fcb 100644 --- a/src/advance/confonding/pointer.md +++ b/src/advance/confonding/pointer.md @@ -1 +1 @@ -# 原生指针、引用和智能指针 todo +# 裸指针、引用和智能指针 todo diff --git a/src/advance/lifetime/advance.md b/src/advance/lifetime/advance.md index d00bb683..014fdf5c 100644 --- a/src/advance/lifetime/advance.md +++ b/src/advance/lifetime/advance.md @@ -136,7 +136,7 @@ error[E0499]: cannot borrow `*map` as mutable more than once at a time 不安全代码(`unsafe`)经常会凭空产生引用或生命周期,这些生命周期被称为是 **无界(unbound)** 的。 -无界生命周期往往是在解引用一个原生指针(裸指针 raw pointer)时产生的,换句话说,它是凭空产生的,因为输入参数根本就没有这个生命周期: +无界生命周期往往是在解引用一个裸指针(裸指针 raw pointer)时产生的,换句话说,它是凭空产生的,因为输入参数根本就没有这个生命周期: ```rust fn f<'a, T>(x: *const T) -> &'a T { diff --git a/src/advance/lifetime/static.md b/src/advance/lifetime/static.md index 89607cbb..619aae5f 100644 --- a/src/advance/lifetime/static.md +++ b/src/advance/lifetime/static.md @@ -57,7 +57,7 @@ fn get_memory_location() -> (usize, usize) { } fn get_str_at_location(pointer: usize, length: usize) -> &'static str { - // 使用原生指针需要 `unsafe{}` 语句块 + // 使用裸指针需要 `unsafe{}` 语句块 unsafe { from_utf8_unchecked(from_raw_parts(pointer as *const u8, length)) } } @@ -68,7 +68,7 @@ fn main() { "The {} bytes at 0x{:X} stored: {}", length, pointer, message ); - // 如果大家想知道为何处理原生指针需要 `unsafe`,可以试着反注释以下代码 + // 如果大家想知道为何处理裸指针需要 `unsafe`,可以试着反注释以下代码 // let message = get_str_at_location(1000, 10); } ``` diff --git a/src/advance/unsafe/intro.md b/src/advance/unsafe/intro.md index 28d5cd57..f13e16e7 100644 --- a/src/advance/unsafe/intro.md +++ b/src/advance/unsafe/intro.md @@ -42,17 +42,17 @@ fn main() { } ``` -上面代码中, `r1` 是一个原生指针(又称裸指针,raw pointer),由于它具有破坏 Rust 内存安全的潜力,因此只能在 `unsafe` 代码块中使用,如果你去掉 `unsafe {}`,编译器会立刻报错。 +上面代码中, `r1` 是一个裸指针(又称裸指针,raw pointer),由于它具有破坏 Rust 内存安全的潜力,因此只能在 `unsafe` 代码块中使用,如果你去掉 `unsafe {}`,编译器会立刻报错。 言归正传, `unsafe` 能赋予我们 5 种超能力,这些能力在安全的 Rust 代码中是无法获取的: -- 解引用原生指针,就如上例所示 +- 解引用裸指针,就如上例所示 - 调用一个 `unsafe` 或外部的函数 - 访问或修改一个可变的[静态变量](https://course.rs/advance/global-variable.html#静态变量) - 实现一个 `unsafe` 特征 - 访问 `union` 中的字段 -在本章中,我们将着重讲解原生指针和 FFI 的使用。 +在本章中,我们将着重讲解裸指针和 FFI 的使用。 ## unsafe 的安全保证 @@ -60,7 +60,7 @@ fn main() { 首先,`unsafe` 并不能绕过 Rust 的借用检查,也不能关闭任何 Rust 的安全检查规则,例如当你在 `unsafe` 中使用**引用**时,该有的检查一样都不会少。 -因此 `unsafe` 能给大家提供的也仅仅是之前的 5 种超能力,在使用这 5 种能力时,编译器才不会进行内存安全方面的检查,最典型的就是使用**原生指针**(引用和原生指针有很大的区别)。 +因此 `unsafe` 能给大家提供的也仅仅是之前的 5 种超能力,在使用这 5 种能力时,编译器才不会进行内存安全方面的检查,最典型的就是使用**裸指针**(引用和裸指针有很大的区别)。 ## 谈虎色变? diff --git a/src/advance/unsafe/superpowers.md b/src/advance/unsafe/superpowers.md index 5be4d80b..aa14868b 100644 --- a/src/advance/unsafe/superpowers.md +++ b/src/advance/unsafe/superpowers.md @@ -2,24 +2,24 @@ 古龙有一部小说,名为"七种兵器",其中每一种都精妙绝伦,令人闻风丧胆,而 `unsafe` 也有五种兵器,它们可以让你拥有其它代码无法实现的能力,同时它们也像七种兵器一样令人闻风丧胆,下面一起来看看庐山真面目。 -## 解引用原生指针 +## 解引用裸指针 -原生指针(raw pointer) 又称裸指针,在功能上跟引用类似,同时它也需要显式地注明可变性。但是又和引用有所不同,原生指针长这样: `*const T` 和 `*mut T`,它们分别代表了不可变和可变。 +裸指针(raw pointer) 又称裸指针,在功能上跟引用类似,同时它也需要显式地注明可变性。但是又和引用有所不同,裸指针长这样: `*const T` 和 `*mut T`,它们分别代表了不可变和可变。 -大家在之前学过 `*` 操作符,知道它可以用于解引用,但是在原生指针 `*const T` 中,这里的 `*` 只是类型名称的一部分,并没有解引用的含义。 +大家在之前学过 `*` 操作符,知道它可以用于解引用,但是在裸指针 `*const T` 中,这里的 `*` 只是类型名称的一部分,并没有解引用的含义。 -至此,我们已经学过三种类似指针的概念:引用、智能指针和原生指针。与前两者不同,原生指针: +至此,我们已经学过三种类似指针的概念:引用、智能指针和裸指针。与前两者不同,裸指针: - 可以绕过 Rust 的借用规则,可以同时拥有一个数据的可变、不可变指针,甚至还能拥有多个可变的指针 - 并不能保证指向合法的内存 - 可以是 `null` - 没有实现任何自动的回收 (drop) -总之,原生指针跟 C 指针是非常像的,使用它需要以牺牲安全性为前提,但我们获得了更好的性能,也可以跟其它语言或硬件打交道。 +总之,裸指针跟 C 指针是非常像的,使用它需要以牺牲安全性为前提,但我们获得了更好的性能,也可以跟其它语言或硬件打交道。 -#### 基于引用创建原生指针 +#### 基于引用创建裸指针 -下面的代码**基于值的引用**同时创建了可变和不可变的原生指针: +下面的代码**基于值的引用**同时创建了可变和不可变的裸指针: ```rust let mut num = 5; @@ -28,9 +28,9 @@ let r1 = &num as *const i32; let r2 = &mut num as *mut i32; ``` -`as` 可以用于强制类型转换,在[之前章节](https://course.rs/basic/converse.html)中有讲解。在这里,我们将引用 `&num / &mut num` 强转为相应的原生指针 `*const i32 / *mut i32`。 +`as` 可以用于强制类型转换,在[之前章节](https://course.rs/basic/converse.html)中有讲解。在这里,我们将引用 `&num / &mut num` 强转为相应的裸指针 `*const i32 / *mut i32`。 -细心的同学可能会发现,在这段代码中并没有 `unsafe` 的身影,原因在于:**创建原生指针是安全的行为,而解引用原生指针才是不安全的行为** : +细心的同学可能会发现,在这段代码中并没有 `unsafe` 的身影,原因在于:**创建裸指针是安全的行为,而解引用裸指针才是不安全的行为** : ```rust fn main() { @@ -44,16 +44,16 @@ fn main() { } ``` -#### 基于内存地址创建原生指针 +#### 基于内存地址创建裸指针 -在上面例子中,我们基于现有的引用来创建原生指针,这种行为是很安全的。但是接下来的方式就不安全了: +在上面例子中,我们基于现有的引用来创建裸指针,这种行为是很安全的。但是接下来的方式就不安全了: ```rust let address = 0x012345usize; let r = address as *const i32; ``` -这里基于一个内存地址来创建原生指针,可以想像,这种行为是相当危险的。试图使用任意的内存地址往往是一种未定义的行为(undefined behavior),因为该内存地址有可能存在值,也有可能没有,就算有值,也大概率不是你需要的值。 +这里基于一个内存地址来创建裸指针,可以想像,这种行为是相当危险的。试图使用任意的内存地址往往是一种未定义的行为(undefined behavior),因为该内存地址有可能存在值,也有可能没有,就算有值,也大概率不是你需要的值。 同时编译器也有可能会优化这段代码,会造成没有任何内存访问发生,甚至程序还可能发生段错误(segmentation fault)。**总之,你几乎没有好的理由像上面这样实现代码,虽然它是可行的**。 @@ -82,7 +82,7 @@ fn main() { "The {} bytes at 0x{:X} stored: {}", length, pointer, message ); - // 如果大家想知道为何处理原生指针需要 `unsafe`,可以试着反注释以下代码 + // 如果大家想知道为何处理裸指针需要 `unsafe`,可以试着反注释以下代码 // let message = get_str_at_location(1000, 10); } ``` @@ -100,13 +100,13 @@ unsafe { } ``` -使用 `*` 可以对原生指针进行解引用,由于该指针的内存安全性并没有任何保证,因此我们需要使用 `unsafe` 来包裹解引用的逻辑(切记,`unsafe` 语句块的范围一定要尽可能的小,具体原因在上一章节有讲)。 +使用 `*` 可以对裸指针进行解引用,由于该指针的内存安全性并没有任何保证,因此我们需要使用 `unsafe` 来包裹解引用的逻辑(切记,`unsafe` 语句块的范围一定要尽可能的小,具体原因在上一章节有讲)。 -以上代码另一个值得注意的点就是:除了使用 `as` 来显式的转换,我们还使用了隐式的转换方式 `let c: *const i32 = &a;`。在实际使用中,我们建议使用 `as` 来转换,因为这种显式的方式更有助于提醒用户:你在使用的指针是原生指针,需要小心。 +以上代码另一个值得注意的点就是:除了使用 `as` 来显式的转换,我们还使用了隐式的转换方式 `let c: *const i32 = &a;`。在实际使用中,我们建议使用 `as` 来转换,因为这种显式的方式更有助于提醒用户:你在使用的指针是裸指针,需要小心。 -#### 基于智能指针创建原生指针 +#### 基于智能指针创建裸指针 -还有一种创建原生指针的方式,那就是基于智能指针来创建: +还有一种创建裸指针的方式,那就是基于智能指针来创建: ```rust let a: Box = Box::new(10); @@ -118,9 +118,9 @@ let c: *const i32 = Box::into_raw(a); #### 小结 -像之前代码演示的那样,使用原生指针可以让我们创建两个可变指针都指向同一个数据,如果使用安全的 Rust,你是无法做到这一点的,违背了借用规则,编译器会对我们进行无情的阻止。因此原生指针可以绕过借用规则,但是由此带来的数据竞争问题,就需要大家自己来处理了,总之,需要小心! +像之前代码演示的那样,使用裸指针可以让我们创建两个可变指针都指向同一个数据,如果使用安全的 Rust,你是无法做到这一点的,违背了借用规则,编译器会对我们进行无情的阻止。因此裸指针可以绕过借用规则,但是由此带来的数据竞争问题,就需要大家自己来处理了,总之,需要小心! -既然这么危险,为何还要使用原生指针?除了之前提到的性能等原因,还有一个重要用途就是跟 `C` 语言的代码进行交互( FFI ),在讲解 FFI 之前,先来看看如何调用 unsafe 函数或方法。 +既然这么危险,为何还要使用裸指针?除了之前提到的性能等原因,还有一个重要用途就是跟 `C` 语言的代码进行交互( FFI ),在讲解 FFI 之前,先来看看如何调用 unsafe 函数或方法。 ## 调用 unsafe 函数或方法 @@ -223,13 +223,13 @@ fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) { 相比安全实现,这段代码就显得没那么好理解了,甚至于我们还需要像 C 语言那样,通过指针地址的偏移去控制数组的分割。 -- `as_mut_ptr` 会返回指向 `slice` 首地址的原生指针 `*mut i32` +- `as_mut_ptr` 会返回指向 `slice` 首地址的裸指针 `*mut i32` - `slice::from_raw_parts_mut` 函数通过指针和长度来创建一个新的切片,简单来说,该切片的初始地址是 `ptr`,长度为 `mid` - `ptr.add(mid)` 可以获取第二个切片的初始地址,由于切片中的元素是 `i32` 类型,每个元素都占用了 4 个字节的内存大小,因此我们不能简单的用 `ptr + mid` 来作为初始地址,而应该使用 `ptr + 4 * mid`,但是这种使用方式并不安全,因此 `.add` 方法是最佳选择 -由于 `slice::from_raw_parts_mut` 使用原生指针作为参数,因此它是一个 `unsafe fn`,我们在使用它时,就必须用 `unsafe` 语句块进行包裹,类似的,`.add` 方法也是如此(还是那句话,不要将无关的代码包含在 `unsafe` 语句块中)。 +由于 `slice::from_raw_parts_mut` 使用裸指针作为参数,因此它是一个 `unsafe fn`,我们在使用它时,就必须用 `unsafe` 语句块进行包裹,类似的,`.add` 方法也是如此(还是那句话,不要将无关的代码包含在 `unsafe` 语句块中)。 -部分同学可能会有疑问,那这段代码我们怎么保证 `unsafe` 中使用的原生指针 `ptr` 和 `ptr.add(mid)` 是合法的呢?秘诀就在于 `assert!(mid <= len);` ,通过这个断言,我们保证了原生指针一定指向了 `slice` 切片中的某个元素,而不是一个莫名其妙的内存地址。 +部分同学可能会有疑问,那这段代码我们怎么保证 `unsafe` 中使用的裸指针 `ptr` 和 `ptr.add(mid)` 是合法的呢?秘诀就在于 `assert!(mid <= len);` ,通过这个断言,我们保证了裸指针一定指向了 `slice` 切片中的某个元素,而不是一个莫名其妙的内存地址。 再回到我们的主题:**虽然 split_at_mut 使用了 `unsafe`,但我们无需将其声明为 `unsafe fn`**,这种情况下就是使用安全的抽象包裹 `unsafe` 代码,这里的 `unsafe` 使用是非常安全的,因为我们从合法数据中创建了的合法指针。 @@ -315,7 +315,7 @@ pub extern "C" fn call_from_c() { ## 实现 unsafe 特征 -说实话,`unsafe` 的特征确实不多见,如果大家还记得的话,我们在之前的 [Send 和 Sync](https://course.rs/advance/concurrency-with-threads/send-sync.html#为原生指针实现sync) 章节中实现过 `unsafe` 特征 `Send`。 +说实话,`unsafe` 的特征确实不多见,如果大家还记得的话,我们在之前的 [Send 和 Sync](https://course.rs/advance/concurrency-with-threads/send-sync.html#为裸指针实现sync) 章节中实现过 `unsafe` 特征 `Send`。 之所以会有 `unsafe` 的特征,是因为该特征至少有一个方法包含有编译器无法验证的内容。`unsafe` 特征的声明很简单: @@ -333,7 +333,7 @@ fn main() {} 通过 `unsafe impl` 的使用,我们告诉编译器:相应的正确性由我们自己来保证。 -再回到刚提到的 `Send` 特征,若我们的类型中的所有字段都实现了 `Send` 特征,那该类型也会自动实现 `Send`。但是如果我们想要为某个类型手动实现 `Send` ,例如为原生指针,那么就必须使用 `unsafe`,相关的代码在之前的链接中也有,大家可以移步查看。 +再回到刚提到的 `Send` 特征,若我们的类型中的所有字段都实现了 `Send` 特征,那该类型也会自动实现 `Send`。但是如果我们想要为某个类型手动实现 `Send` ,例如为裸指针,那么就必须使用 `unsafe`,相关的代码在之前的链接中也有,大家可以移步查看。 总之,`Send` 特征标记为 `unsafe` 是因为 Rust 无法验证我们的类型是否能在线程间安全的传递,因此就需要通过 `unsafe` 来告诉编译器,它无需操心,剩下的交给我们自己来处理。 diff --git a/src/appendix/keywords.md b/src/appendix/keywords.md index 2f53b443..70f33010 100644 --- a/src/appendix/keywords.md +++ b/src/appendix/keywords.md @@ -26,7 +26,7 @@ - `match` - 模式匹配 - `mod` - 定义一个模块 - `move` - 使闭包获取其所捕获项的所有权 -- `mut` - 在引用、原生指针或模式绑定中使用,表明变量是可变的 +- `mut` - 在引用、裸指针或模式绑定中使用,表明变量是可变的 - `pub` - 表示结构体字段、`impl` 块或模块的公共可见性 - `ref` - 通过引用绑定 - `return` - 从函数中返回 diff --git a/src/appendix/operators.md b/src/appendix/operators.md index 6db959ac..de57084c 100644 --- a/src/appendix/operators.md +++ b/src/appendix/operators.md @@ -25,7 +25,7 @@ | `*` | `expr * expr` | 算术乘法 | `Mul` | | `*=` | `var *= expr` | 算术乘法与赋值 | `MulAssign` | | `*` | `*expr` | 解引用 | | -| `*` | `*const type`, `*mut type` | 原生指针 | | +| `*` | `*const type`, `*mut type` | 裸指针 | | | `+` | `trait + trait`, `'a + trait` | 复合类型限制 | | | `+` | `expr + expr` | 算术加法 | `Add` | | `+=` | `var += expr` | 算术加法与赋值 | `AddAssign` | diff --git a/src/async-rust/async/pin-unpin.md b/src/async-rust/async/pin-unpin.md index c2752f6b..8932155a 100644 --- a/src/async-rust/async/pin-unpin.md +++ b/src/async-rust/async/pin-unpin.md @@ -16,7 +16,7 @@ struct SelfRef { } ``` -在上面的结构体中,`pointer_to_value` 是一个原生指针,指向第一个字段 `value` 持有的字符串 `String` 。很简单对吧?现在考虑一个情况, 若`String` 被移动了怎么办? +在上面的结构体中,`pointer_to_value` 是一个裸指针,指向第一个字段 `value` 持有的字符串 `String` 。很简单对吧?现在考虑一个情况, 若`String` 被移动了怎么办? 此时一个致命的问题就出现了:新的字符串的内存地址变了,而 `pointer_to_value` 依然指向之前的地址,一个重大 bug 就出现了! @@ -168,7 +168,7 @@ impl Test { } ``` -`Test` 提供了方法用于获取字段 `a` 和 `b` 的值的引用。这里`b` 是 `a` 的一个引用,但是我们并没有使用引用类型而是用了原生指针,原因是:Rust 的借用规则不允许我们这样用,因为不符合生命周期的要求。 此时的 `Test` 就是一个自引用结构体。 +`Test` 提供了方法用于获取字段 `a` 和 `b` 的值的引用。这里`b` 是 `a` 的一个引用,但是我们并没有使用引用类型而是用了裸指针,原因是:Rust 的借用规则不允许我们这样用,因为不符合生命周期的要求。 此时的 `Test` 就是一个自引用结构体。 如果不移动任何值,那么上面的例子将没有任何问题,例如: diff --git a/src/basic/converse.md b/src/basic/converse.md index 6ff7a47c..6d2f6a0b 100644 --- a/src/basic/converse.md +++ b/src/basic/converse.md @@ -282,11 +282,11 @@ impl Clone for Container { 你以为你之前凝视的是深渊吗?不,你凝视的只是深渊的大门。 `mem::transmute_copy` 才是真正的深渊,它比之前的还要更加危险和不安全。它从 `T` 类型中拷贝出 `U` 类型所需的字节数,然后转换成 `U`。 `mem::transmute` 尚有大小检查,能保证两个数据的内存大小一致,现在这哥们干脆连这个也丢了,只不过 `U` 的尺寸若是比 `T` 大,会是一个未定义行为。 -当然,你也可以通过原生指针转换和 `unions` (todo!)获得所有的这些功能,但是你将无法获得任何编译提示或者检查。原生指针转换和 `unions` 也不是魔法,无法逃避上面说的规则。 +当然,你也可以通过裸指针转换和 `unions` (todo!)获得所有的这些功能,但是你将无法获得任何编译提示或者检查。裸指针转换和 `unions` 也不是魔法,无法逃避上面说的规则。 `transmute` 虽然危险,但作为一本工具书,知识当然要全面,下面列举两个有用的 `transmute` 应用场景 :)。 -- 将原生指针变成函数指针: +- 将裸指针变成函数指针: ```rust fn foo() -> i32 { @@ -295,7 +295,7 @@ fn foo() -> i32 { let pointer = foo as *const (); let function = unsafe { - // 将原生指针转换为函数指针 + // 将裸指针转换为函数指针 std::mem::transmute::<*const (), fn() -> i32>(pointer) }; assert_eq!(function(), 0); diff --git a/src/too-many-lists/unsafe-queue/basics.md b/src/too-many-lists/unsafe-queue/basics.md new file mode 100644 index 00000000..02dea623 --- /dev/null +++ b/src/too-many-lists/unsafe-queue/basics.md @@ -0,0 +1,5 @@ +# 基本操作 + +> 本章节的代码中有一个隐藏的 bug,因为它藏身于 unsafe 中,因此不会导致报错,我们会在后续章节解决这个问题,所以,请不要在生产环境使用此处的代码 + +在开始之前,大家需要先了解 unsafe 的[相关知识](https://course.rs/advance/unsafe/intro.html), \ No newline at end of file diff --git a/内容变更记录.md b/内容变更记录.md index 20c51ac8..b4734226 100644 --- a/内容变更记录.md +++ b/内容变更记录.md @@ -1,8 +1,10 @@ # ChangeLog 记录一些值得注意的变更。 + ## 2022-03-17 +- 新增章节: [不错的unsafe队列-数据布局](https://course.rs/too-many-lists/unsafe-queue/layout.html) - 新增章节: [deque-迭代器](https://course.rs/too-many-lists/deque/iterator.html) - 新增章节: [deque-最终代码](https://course.rs/too-many-lists/deque/final-code.html)