From b55ec37295398287fdc8502eaad1737ca9f57acb Mon Sep 17 00:00:00 2001 From: xfy Date: Tue, 18 Mar 2025 09:57:13 +0800 Subject: [PATCH] new posts --- content/posts/chapter-7-error-handling.mdx | 70 ++++++++++ content/posts/chapter-8-crate-and-module.mdx | 16 +++ content/posts/chapter-9-struct.mdx | 137 +++++++++++++++++++ 3 files changed, 223 insertions(+) create mode 100644 content/posts/chapter-7-error-handling.mdx create mode 100644 content/posts/chapter-8-crate-and-module.mdx create mode 100644 content/posts/chapter-9-struct.mdx diff --git a/content/posts/chapter-7-error-handling.mdx b/content/posts/chapter-7-error-handling.mdx new file mode 100644 index 0000000..4439fd3 --- /dev/null +++ b/content/posts/chapter-7-error-handling.mdx @@ -0,0 +1,70 @@ +--- +title: 第 7 章 错误处理 +date: '2024-12-12' +tags: [Rust] +--- + +Rust 中有两类常见的错误处理:`panic` 和 `Result`。 + +普通错误使用 `Result` 类型来处理。`Result` 通常用以表示由程序外部的事物引发的错误,比如错误的输入、网络的中断或权限问题。 + +`panic` 针对的是另一种错误,即那种永远不应该发生的错误。 + +> Rust 之所以会用一个新词(panic)而不是沿用“异常”来表达,是因为两者并不等价。 + +## panic + +当程序遇到下列问题时,就可以断定程序自身存在 Bug,故而引发 panic: + +- 数组越界访问; +- 整数除以 0; +- 在恰好为 `Err` 的 `Result` 上调用 `.expect()`; +- 断言失败; + +在 panic 时,Rust 为我们提供了一种选择,展开调用栈或者终止进程。展开调用栈是默认方案。 + +### 展开调用栈 + +如果在 Rust 中除以了 0,就会触发 panic,通常按如下方式处理: + +```rust +#[allow(unconditional_panic)] +fn main() { + let x = 37 / 0; +} +``` + +- 把一条错误信息打印到终端。 + +```shell +thread 'main' panicked at main.rs:3:13: +attempt to divide by zero +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +``` + +如果设置了 `RUST_BACKTRACE=1` 环境变量,那么就像这条消息打印的一样,Rust 也会在这里转储当前调用栈。 + +```shell +❯ RUST_BACKTRACE=1 ./main +thread 'main' panicked at main.rs:3:13: +attempt to divide by zero +stack backtrace: + 0: rust_begin_unwind + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/std/src/panicking.rs:645:5 + 1: core::panicking::panic_fmt + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/panicking.rs:72:14 + 2: core::panicking::panic + at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library/core/src/panicking.rs:145:5 + 3: main::main + 4: core::ops::function::FnOnce::call_once +note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. +``` + +- 展开调用栈。这很像 C++ 的异常处理。 + 当前函数使用的任何临时值、局部变量或参数都会按照与创建它们时相反的顺序被丢弃。丢弃一个值仅仅意味着随后会进行清理:程序正在使用的任何字符串或者向量都将被释放,所有打开的文件都将被关闭,等等。还会调用由用户定义的 `drop` 方法。 + +清理了当前函数调用后,我们将继续执行到其调用者中,以相同的方式丢弃其变量和参数。然后再“走到”那个调用者的调用者中,在调用栈中逐级向上,以此类推。 + +panic 是安全的,没有违反 Rust 的任何安全规则,即使你故意在标准库方法的中间引发 panic,它也永远不会在内存中留下悬空指针或半初始化的值。 + +panic 是基于线程的,一个线程 panic 时,其他线程可以继续做自己的事。 diff --git a/content/posts/chapter-8-crate-and-module.mdx b/content/posts/chapter-8-crate-and-module.mdx new file mode 100644 index 0000000..4ccd33c --- /dev/null +++ b/content/posts/chapter-8-crate-and-module.mdx @@ -0,0 +1,16 @@ +--- +title: 第 8 章 Create 与模块 +date: '2025-01-12' +tags: [Rust] +--- + +有时,可能需要对函数的内联进行微观管理,但我们通常会把这种优化留给编译器。可以使用 `#[inline]` 属性进行微观管理: + +```rust +#[inline] +fn add(x: i32, y: i32) -> i32 { + x + y +} +``` + +只有在一种特定的情况下,如果没有 `#[inline]`,就不会发生内联。当一个 crate 中定义的函数或方法在另一个 crate 中被调用时,Rust 不会将其内联,除非它是泛型的或名曲的标记为 `#[inline]`。 diff --git a/content/posts/chapter-9-struct.mdx b/content/posts/chapter-9-struct.mdx new file mode 100644 index 0000000..7aaab03 --- /dev/null +++ b/content/posts/chapter-9-struct.mdx @@ -0,0 +1,137 @@ +--- +title: 第 9 章 结构体 +date: '2025-02-12' +tags: [Rust] +--- + +Rust 有 3 种结构体类型:具名字段型结构体、元组结构体和单元型结构体。 + +- 具名型结构体:为每个组件命名。 +- 元组型结构体:按组件出现的顺序标识他们。 +- 单元型结构体:没有组件。 + +## 具名结构体 + +```rust +struct GrayscaleMap { + pixels: Vec, + size: (usize, usize), +} +``` + +与其他所有语法项一样,结构体默认情况下是私有的,仅在声明它们的模块及其子模块种可见。可以通过在结构体的定义前加上 `pub` 来是结构体在其模块外部可见。结构体中的每个字段也是默认私有的。 + +```rust +pub struct GrayscaleMap { + pub pixels: Vec, + pub size: (usize, usize), +} +``` + +其他模块不能按名称访问私有字段或使用结构体表达式来创建新的 `GrayscaleMap` 值。 + +在结构体表达式中,如果具有名字的字段后面跟着 `.. EXPR`,则任何未提及的字段都会从 `EXPRE`(必须是相同结构体的另一个值)中获取它们的值。 + +```rust +struct Broom { + name: String, + height: u32, + health: u32, + position: (f32, f32, f32), + intent: BroomIntent, +} + +#[derive(Copy, Clone)] +enum BroomIntent { + FetchWater, + DumpWater, +} + +fn chop(b: Broom) -> (Broom, Broom) { + let mut broom1 = Broom { + height: b.height / 2, + ..b + }; + + let mut broom2 = Broom { + name: broom1.name.clone(), + ..broom1 + }; + + broom1.name.push_str(" I"); + broom2.name.push_str(" II"); + + (broom1, broom2) +} +``` + +## 元组型结构体 + +元组型结构体顾名思义类似于元组: + +```rust +struct Bounds(usize, usize); +``` + +表达式 `Bounds(1024, 768)` 看起来像一个函数调用,实际上它确实是,级定义这种类型时也隐式定义了一个函数: + +```rust +fn Bounds(elem0: usize, elem1: uszie) -> Bounds { ... } +``` + +元组型结构体适用于创造**新类型**(newtype),即建立一个只包含单组件的结构体,以获得更严格的类型检查。 + +## 单元型结构体 + +单元型结构体声明了一个根本没有元素的结构体类型: + +```rust +struct onesuch; +``` + +这种类型的值不占用内存,很像单元类型 `()`。Rust 既不会在内存中实际存储单元类型的值,也不会生成代码对它们进行操作,因为仅通过值的类型它就能知道关于值的所有信息 + +## 结构体布局 + +在内存中,具名字段结构体和元组型结构体是一样的:值的集合以特定的方式在内存中布局。 + +```rust +struct GrayscaleMap { + pixels: Vec, + size: (usize, usize), +} +``` + +与 C 和 C++ 不同,Rust 没有具体承诺它将如何在内存中对结构体的字段或元素进行排序。然而,Rust 确实承诺会将字段的值直接存储在结构体本身的内存块中。JavaScript、Python 和 Java 会将 `pixels` 值和 `size` 值分别放在它们自己的分配的堆上的块中,并让 `GrayscaleMap` 的字段指向它们,而 Rust 会将 `pixel` 值和 `size` 值直接嵌入在 `GrayscaleMap` 值中。只有由 `pixels` 向量拥有的在堆上分配的缓冲区才会留在它自己的块中。 + +可以使用 `#[repr(C)]` 属性要求 Rust 以兼容 C 和 C++ 的方式对结构体进行布局。 + +## 方法 + +在 `impl` 块中定义的函数被称为关联函数,因为它们是与特定类型相关联的。与关联函数相对应的是自由函数,它是未定义在 `impl` 块中的语法项。 + +`impl` 块知识定义 `fn` 的集合,每个定义都会成为块顶部命名的结构体类型上的一个方法。 + +当调用一个需要引用的方法时,我们不需要自己借用可变引用,常规的方法调用语法已经隐式的处理了这一点。 + +```rust +struct Queue { + older: Vec, + younger: Vec, +} + +impl Queue { + pub fn push(&mut self, item: T) { + self.younger.push(item); + } +} + +let mut q = Queue { + older: vec![1], + younger: vec![], +}; + +q.push(2); +``` + +只需要编写 `q.push()` 就可以借入对 `q` 的可变引用,就好像写的是 `(&mut q).push()` 一样,因为这是 `push` 方法的 `self` 参数所要求的。