From 15e239119097fcfeeeea53d1c8ba965f459b2548 Mon Sep 17 00:00:00 2001 From: shiyivei Date: Tue, 27 Jun 2023 09:49:37 +0800 Subject: [PATCH] update advanced types --- module-three/src/type_advance/boxs.rs | 48 ++++-- module-three/src/type_advance/mod.rs | 3 +- .../src/type_advance/mut_container.rs | 102 +++++++------ module-three/src/type_advance/phantomdata.rs | 51 +++++++ module-three/src/type_advance/pin.rs | 46 ++++++ module-three/src/type_advance/special_type.rs | 60 -------- rust-co-learn.md | 144 +++++++++++++----- 7 files changed, 304 insertions(+), 150 deletions(-) create mode 100644 module-three/src/type_advance/phantomdata.rs create mode 100644 module-three/src/type_advance/pin.rs delete mode 100644 module-three/src/type_advance/special_type.rs diff --git a/module-three/src/type_advance/boxs.rs b/module-three/src/type_advance/boxs.rs index f2cff67..c2605e4 100644 --- a/module-three/src/type_advance/boxs.rs +++ b/module-three/src/type_advance/boxs.rs @@ -4,24 +4,50 @@ /** ``` - // 1 Box 与数据分配 +// 1 Box 与数据分配 - // 在Rust中,你可以使用Box将数据强行存储到堆上 +// 在Rust中,你可以使用Box将数据强行存储到堆上 - let a = Box::new("rust"); - let b = Box::new(42); +let a = Box::new("rust"); +let b = Box::new(42); - // 它也是唯一可以将数据放到堆上的途径 +// 它也是唯一可以将数据放到堆上的途径 - // 2 Box 是一个智能指针 - // 它实现了Deref和Drop trait +// 2 Box 是一个智能指针 +// 它实现了Deref和Drop trait - let s = Box::new("rust"); - let s = *s; // 解引用 +let s = Box::new("rust"); +let s = *s; // 解引用 - // 离开作用域时,会自动调用drop方法,释放堆上的数据 +// 离开作用域时,会自动调用drop方法,释放堆上的数据 - // 这个类型比较简单,再次需要强调的是它是众多的Rust基于结构体构和trait造的特殊类型之一 +// 这个类型比较简单,再次需要强调的是它是众多的Rust基于结构体构和trait造的特殊类型之一 + +// 3 为什么要把数据存放在堆上?一个链表例子 + +// 定义链表节点数据结构 +enum ListNode { + Cons(T, Box>), + Nil, +} +// 声明三个节点 +let node3 = ListNode::Cons(3, Box::new(ListNode::Nil)); +let node2 = ListNode::Cons(2, Box::new(node3)); +let list = ListNode::Cons(1, Box::new(node2)); + +// let list: ListNode = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil)))))); + +// 使用模式匹配解构节点值 +match list { + ListNode::Cons(head, tail) => { + println!("head: {}", head); + match *tail { + ListNode::Cons(head, _) => println!("second item: {}", head), + ListNode::Nil => println!("there is no second item"), + } + } + ListNode::Nil => println!("list is empty"), +} ``` */ diff --git a/module-three/src/type_advance/mod.rs b/module-three/src/type_advance/mod.rs index ffca666..fad32ce 100644 --- a/module-three/src/type_advance/mod.rs +++ b/module-three/src/type_advance/mod.rs @@ -3,4 +3,5 @@ pub mod boxs; pub mod mut_container; -pub mod special_type; +pub mod phantomdata; +pub mod pin; diff --git a/module-three/src/type_advance/mut_container.rs b/module-three/src/type_advance/mut_container.rs index 46ef1c6..5d3a181 100644 --- a/module-three/src/type_advance/mut_container.rs +++ b/module-three/src/type_advance/mut_container.rs @@ -5,50 +5,64 @@ ``` - // 1.编译期:通过 mut 显式声明变量的可变性,也叫外部可变性 - use std::cell::Cell; - let can_not_change = "rust"; - let mut can_change = "go"; - // can_not_change = "cpp"; // 不可重新赋值 - can_change = "c"; // 可以更改 - - // 2. 运行期:通过Cell和RefCell实现可变性,也叫内部可变性 - // 2.1 Cell 的修改和读取 - struct Foo { - x: u32, - y: Cell, - z: Cell>, - } - - let foo = Foo { - x: 1, - y: Cell::new(3), - z: Cell::new(Vec::new()), - }; - - // 修改容器内的变量使用set方法 - foo.y.set(100); - foo.z.set(vec!["rust".to_owned()]); - - // 读取容器内的变量有两种:固定大小类型可以使用 get和into_inner; 动态大小类型只能使用into_inner - assert_eq!(100, foo.y.get()); - assert_eq!(100, foo.y.into_inner()); - - // assert_eq!(vec!["rust".to_owned()], foo.z.get()); 不能使用get方法 - assert_eq!(vec!["rust".to_owned()], foo.z.into_inner()); - - // 2.2 RefCell 的修改和读取 - // 通过borrow_mut实现可变性 - // 主要是应用于一些动态大小类型,通过borrow获取值,有运行时开销 - - use std::cell::RefCell; - let vec = vec![1, 2, 3, 4]; - - let ref_vec = RefCell::new(vec); - - println!("{:?}", ref_vec.borrow()); // 不可变借用 使用borrow - ref_vec.borrow_mut().push(5); // 可变借用改变,使用borrow_mut - println!("{:?}", ref_vec.borrow()); +// 1.编译期:通过 mut 显式声明变量的可变性,也叫外部可变性 +use std::cell::Cell; +let can_not_change = "rust"; +let mut can_change = "go"; +// can_not_change = "cpp"; // 不可重新赋值 +can_change = "c"; // 可以更改 + +// 2 一个需要改变不可变变量的例子 + +// let var1 = 0; +// let mut var2 = 0; + +// while var2 <= 10 { +// if var2 == 10 { +// var1 = 10; +// } +// var2 += 1; +// } + +// println!("var1: {}, var2: {}", var1, var2); + +// 3. 运行期:通过Cell和RefCell实现可变性,也叫内部可变性 +// 3.1 Cell 的修改和读取 +struct Foo { + x: u32, + y: Cell, + z: Cell>, +} + +let foo = Foo { + x: 1, + y: Cell::new(3), + z: Cell::new(Vec::new()), +}; + +// 修改容器内的变量使用set方法 +foo.y.set(100); +foo.z.set(vec!["rust".to_owned()]); + +// 读取容器内的变量有两种:固定大小类型可以使用 get和into_inner; 动态大小类型只能使用into_inner +assert_eq!(100, foo.y.get()); +assert_eq!(100, foo.y.into_inner()); + +// assert_eq!(vec!["rust".to_owned()], foo.z.get()); 不能使用get方法 +assert_eq!(vec!["rust".to_owned()], foo.z.into_inner()); + +// 3.2 RefCell 的修改和读取 +// 通过borrow_mut实现可变性 +// 主要是应用于一些动态大小类型,通过borrow获取值,有运行时开销 + +use std::cell::RefCell; +let vec = vec![1, 2, 3, 4]; + +let ref_vec = RefCell::new(vec); + +println!("{:?}", ref_vec.borrow()); // 不可变借用 使用borrow +ref_vec.borrow_mut().push(5); // 可变借用改变,使用borrow_mut +println!("{:?}", ref_vec.borrow()); ``` */ diff --git a/module-three/src/type_advance/phantomdata.rs b/module-three/src/type_advance/phantomdata.rs new file mode 100644 index 0000000..ad72621 --- /dev/null +++ b/module-three/src/type_advance/phantomdata.rs @@ -0,0 +1,51 @@ +//! 2.3 特殊类型 PhantomData +//! + +/** + +``` +use std::marker::PhantomData; + use std::ops::Deref; + + struct MyType { + data: *const T, + _marker: PhantomData, + } + + impl MyType { + fn new(t: T) -> MyType { + MyType { + data: &t, + _marker: PhantomData, + } + } + } + + impl Deref for MyType { + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.data } + } + } + + impl Drop for MyType { + fn drop(&mut self) { + println!("Dropping MyType instance!"); + } + } + + let resource: MyType = MyType::new(true); + let another_resource: MyType = MyType::new(32); + + print!("{:?}", unsafe { *(resource.data) }); + print!(" {:?}", unsafe { *(another_resource.data) }); + + let my_instance: MyType = MyType::new(33); + // 执行到这里时,my_instance 将会离开作用域并被销毁,调用我们自定义的 drop 方法。 +``` +*/ + +pub fn phantomdata() { + println!(""); +} diff --git a/module-three/src/type_advance/pin.rs b/module-three/src/type_advance/pin.rs new file mode 100644 index 0000000..8d7605b --- /dev/null +++ b/module-three/src/type_advance/pin.rs @@ -0,0 +1,46 @@ +//! 2.4 特殊类型 Pin、PhantomPinned +//! + +/** + +``` + use std::marker::PhantomPinned; + use std::pin::Pin; + +// 定义一个自引用的结构体。因为它含有指向自身的指针,所以它在内存中不能被移动。 +struct SelfReferential { + i: i32, + p: *const i32, // 裸指针,将会指向上述的 i + _pin: PhantomPinned, // 也是一个零大小的标记类型,阻止 Rust 自动为我们的类型实现 Unpin trait +} + +// 注意此时 p 是一个空指针,我们还没有为它分配指向的地址 +let mut test = SelfReferential { + i: 123, + p: std::ptr::null(), + _pin: PhantomPinned, +}; + +// 使用 Pin 包装我们的结构体实例。这样就能保证 test 的内存地址不会在其生命周期中改变。 +// 注意:这里使用了 unsafe,因为我们需要保证在 test 被包装为 Pin 后,其地址不会被改变 +let mut test = unsafe { Pin::new_unchecked(&mut test) }; + +// 创建一个裸指针,指向 test 的 i 字段。注意我们使用了 test 的引用版本以保证安全。 +let self_ptr: *const i32 = &test.as_ref().get_ref().i; + +// 将裸指针存储到 test 的 p 字段。注意我们使用了 unsafe,因为我们正在直接修改内存。 + +unsafe { + let mut_ref = Pin::as_mut(&mut test); + mut_ref.get_unchecked_mut().p = self_ptr; +} + +// 打印 test 的 p 字段所指向的内容。注意我们使用了 unsafe,因为我们正在解引用裸指针。 +let val = unsafe { *(test.as_ref().get_ref().p) }; +println!("val: {}", val); // 输出 "val: 123" +``` +*/ + +pub fn pin() { + println!(""); +} diff --git a/module-three/src/type_advance/special_type.rs b/module-three/src/type_advance/special_type.rs deleted file mode 100644 index adb6999..0000000 --- a/module-three/src/type_advance/special_type.rs +++ /dev/null @@ -1,60 +0,0 @@ -//! 2.4 特殊类型 -//! - -/** - -``` - -fn main() { - -// 1 特殊类型:PhantomData - - use std::marker::PhantomData; - - struct MyType { - _marker: PhantomData, - } - - impl MyType { - fn new() -> MyType { - MyType { - _marker: PhantomData, - } - } - } - - fn main() { - let a: MyType = MyType::new(); - let b: MyType = MyType::new(); - } - - // 2 特殊类型:Pin - - use std::pin::Pin; - - struct MType { - data: String, - } - - impl MType { - fn new(data: String) -> MType { - MType { data } - } - - fn get_data(self: Pin<&Self>) -> &str { - unsafe { &self.get_ref().data } - } - } - - let my_type = MType::new("hello".to_string()); - let pinned = Pin::new(&my_type); - let data = pinned.get_data(); - println!("{}", data); - -} -``` -*/ - -pub fn special_types() { - println!(""); -} diff --git a/rust-co-learn.md b/rust-co-learn.md index e58ec99..6e3e749 100644 --- a/rust-co-learn.md +++ b/rust-co-learn.md @@ -2310,7 +2310,7 @@ Rust 中对于提供了很多类型,用于处理一些特殊的场景 Box 可以将内存强制分配到堆上,并且它也是智能指针,可以自动解引用和管理堆内存。所以在使用的时候只需要使用它将数据分配到堆上,并不需要再考虑如何释放内存 ``` - // 1 Box 与数据分配 +// 1 Box 与数据分配 // 在Rust中,你可以使用Box将数据强行存储到堆上 @@ -2329,6 +2329,31 @@ Box 可以将内存强制分配到堆上,并且它也是智能指针,可以 // 这个类型比较简单,再次需要强调的是它是众多的Rust基于结构体构和trait造的特殊类型之一 + // 3 为什么要把数据存放在堆上?一个链表例子 + + // 定义链表节点数据结构 + enum ListNode { + Cons(T, Box>), + Nil, + } + // 声明三个节点 + let node3 = ListNode::Cons(3, Box::new(ListNode::Nil)); + let node2 = ListNode::Cons(2, Box::new(node3)); + let list = ListNode::Cons(1, Box::new(node2)); + + // let list: ListNode = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil)))))); + + // 使用模式匹配解构节点值 + match list { + ListNode::Cons(head, tail) => { + println!("head: {}", head); + match *tail { + ListNode::Cons(head, _) => println!("second item: {}", head), + ListNode::Nil => println!("there is no second item"), + } + } + ListNode::Nil => println!("list is empty"), + } ``` ### 3.2.2 可变容器 @@ -2336,15 +2361,29 @@ Box 可以将内存强制分配到堆上,并且它也是智能指针,可以 在编译期,我们需要使用 mut 显式声明变量的可变性。在运行时,Rust 提供了可变容器 Cell 和 RefCell 允许修改不可变变量(这个过程实际上是通过原生指针来完成的) ```rust - // 1.编译期:通过 mut 显式声明变量的可变性,也叫外部可变性 +// 1.编译期:通过 mut 显式声明变量的可变性,也叫外部可变性 use std::cell::Cell; let can_not_change = "rust"; let mut can_change = "go"; // can_not_change = "cpp"; // 不可重新赋值 can_change = "c"; // 可以更改 - // 2. 运行期:通过Cell和RefCell实现可变性,也叫内部可变性 - // 2.1 Cell 的修改和读取 + // 2 一个需要改变不可变变量的例子 + + // let var1 = 0; + // let mut var2 = 0; + + // while var2 <= 10 { + // if var2 == 10 { + // var1 = 10; + // } + // var2 += 1; + // } + + // println!("var1: {}, var2: {}", var1, var2); + + // 3. 运行期:通过Cell和RefCell实现可变性,也叫内部可变性 + // 3.1 Cell 的修改和读取 struct Foo { x: u32, y: Cell, @@ -2368,7 +2407,7 @@ Box 可以将内存强制分配到堆上,并且它也是智能指针,可以 // assert_eq!(vec!["rust".to_owned()], foo.z.get()); 不能使用get方法 assert_eq!(vec!["rust".to_owned()], foo.z.into_inner()); - // 2.2 RefCell 的修改和读取 + // 3.2 RefCell 的修改和读取 // 通过borrow_mut实现可变性 // 主要是应用于一些动态大小类型,通过borrow获取值,有运行时开销 @@ -2392,59 +2431,96 @@ Box 可以将内存强制分配到堆上,并且它也是智能指针,可以 一般它起两个作用: -1 用于在类型签名中传递类型信息,但不实际使用 +1 用于在类型签名中传递类型信息,表示一种假象“拥有关系” 2 作为一个类型参数的标记,用于告诉 Rust 编译器某些重要信息,例如,当需要实现 `Drop` trait 时,但是类型不实际包含任何需要释放的资源,可以使用 `PhantomData` 来占据一个虚拟的位置,这样以确保编译器不会优化掉的 `Drop` 实现 ```rust -use std::marker::PhantomData; + use std::marker::PhantomData; + use std::ops::Deref; -struct MyType { - _marker: PhantomData, -} + struct MyType { + data: *const T, + _marker: PhantomData, + } -impl MyType { - fn new() -> MyType { - MyType { _marker: PhantomData } + impl MyType { + fn new(t: T) -> MyType { + MyType { + data: &t, + _marker: PhantomData, + } + } } -} -fn main() { - let a: MyType = MyType::new(); - let b: MyType = MyType::new(); -} + impl Deref for MyType { + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.data } + } + } + + impl Drop for MyType { + fn drop(&mut self) { + println!("Dropping MyType instance!"); + } + } + + let resource: MyType = MyType::new(true); + let another_resource: MyType = MyType::new(32); + + print!("{:?}", unsafe { *(resource.data) }); + print!(" {:?}", unsafe { *(another_resource.data) }); + + let my_instance: MyType = MyType::new(33); + // 执行到这里时,my_instance 将会离开作用域并被销毁,调用我们自定义的 drop 方法。 ``` **`Pin`**: 通常用于解决 Rust 引用类型的安全性问题,尤其是与异步编程和内存管理相关的问题。`Pin` 类型可以确保被引用的值不会被移动或释放 -一般情况下:Rust 会确保引用总是有效的,但是也有例外情况(没办法,又涉及到后面的内容了,这里你可以先大概有个了解就行): +一般情况下:Rust 会确保引用总是有效的,但是也有例外情况: -1. 当异步代码在运行时可能会移动或释放被引用的值时,比如 Future 或 async 闭包 +1. 当异步代码在运行时可能会移动或释放被引用的值时,比如 Future 或 async 闭包(这个我们后面再讲) 2. 当使用 `unsafe` 代码时,可能会通过裸指针将引用类型转换为可变引用类型,从而破坏编译器对引用类型的保护 ```rust - // 2 特殊类型:Pin +// 2 特殊类型:Pin + use std::marker::PhantomPinned; use std::pin::Pin; - struct MType { - data: String, + // 定义一个自引用的结构体。因为它含有指向自身的指针,所以它在内存中不能被移动。 + struct SelfReferential { + i: i32, + p: *const i32, // 裸指针,将会指向上述的 i + _pin: PhantomPinned, // 也是一个零大小的标记类型,阻止 Rust 自动为我们的类型实现 Unpin trait } - impl MType { - fn new(data: String) -> MType { - MType { data } - } + // 注意此时 p 是一个空指针,我们还没有为它分配指向的地址 + let mut test = SelfReferential { + i: 123, + p: std::ptr::null(), + _pin: PhantomPinned, + }; - fn get_data(self: Pin<&Self>) -> &str { - unsafe { &self.get_ref().data } - } + // 使用 Pin 包装我们的结构体实例。这样就能保证 test 的内存地址不会在其生命周期中改变。 + // 注意:这里使用了 unsafe,因为我们需要保证在 test 被包装为 Pin 后,其地址不会被改变 + let mut test = unsafe { Pin::new_unchecked(&mut test) }; + + // 创建一个裸指针,指向 test 的 i 字段。注意我们使用了 test 的引用版本以保证安全。 + let self_ptr: *const i32 = &test.as_ref().get_ref().i; + + // 将裸指针存储到 test 的 p 字段。注意我们使用了 unsafe,因为我们正在直接修改内存。 + + unsafe { + let mut_ref = Pin::as_mut(&mut test); + mut_ref.get_unchecked_mut().p = self_ptr; } - let my_type = MType::new("hello".to_string()); - let pinned = Pin::new(&my_type); - let data = pinned.get_data(); - println!("{}", data); + // 打印 test 的 p 字段所指向的内容。注意我们使用了 unsafe,因为我们正在解引用裸指针。 + let val = unsafe { *(test.as_ref().get_ref().p) }; + println!("val: {}", val); // 输出 "val: 123" ``` ## 3.4 课后习题