From efe815761c7a3c295b870f3b015931728efcbea5 Mon Sep 17 00:00:00 2001 From: xfy Date: Thu, 24 Oct 2024 09:19:03 +0800 Subject: [PATCH] update post chapter-5-reference --- content/posts/chapter-5-reference.mdx | 124 ++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 content/posts/chapter-5-reference.mdx diff --git a/content/posts/chapter-5-reference.mdx b/content/posts/chapter-5-reference.mdx new file mode 100644 index 0000000..cadb927 --- /dev/null +++ b/content/posts/chapter-5-reference.mdx @@ -0,0 +1,124 @@ +--- +title: 第 5 章 引用 +date: '2024-10-10' +tags: [Rust] +--- + +Rust 中的指针分为拥有型指针和非拥有型指针。其中,拥有型指针意味着当拥有者被丢弃时,它的引用目标也会随之消失。引用(reference)的非拥有型指针,意味着对引用目标的生命周期毫无影响。 + +事实上,引用的生命周期绝不能超过其引用目标。为了强调这一点,Rust 把创建对某个值的引用的操作称为借用(borrow)那个值:凡是借用,终须归还。 + +为了在不影响其所有权的情况下访问值。引用分为一下两种。 + +- 共享引用允许我们读取但不能修改其引用目标。但是,共享引用可以同时存在任意数量个。共享引用是 Copy 类型。 +- 可变引用允许我们读取和修改值。但是,一旦一个值拥有了比可变引用,就无法再对该值创建其他任何种类的引用了。可变引用同时只能存在一个。可变引用不是 Copy 类型。 + +共享引用和可变引用之间的区别可以视为在编译器强制执行“多重读取”或“单一写入”规则的一种手段。 + +当通过将值的所有权转移给函数的方式将这个值传递给函数时,就可以说按值传递了它。如果将值的引用传递给函数,就可以说按引用传递了它。 + +如果需要用一个值来表示对某个“可能不存在”事物的引用,请使用类型 `Option<&T>`。在机器码级别,Rust 会将 None 表示为空指针,将 `Some(r)` 表示为非零地址(其中 `r` 是 `&T` 型的值),因此 `Option<&T>` 和 C 或 C++ 中的可空指针一样高效,但更安全:它的类型要求我们在使用之前必须检查它是否为 None。 + +除了对简单地址的引用,Rust 还包括两种胖指针,即携带某个值地址的双字值,以及要正确使用该值所需的某些额外信息。 + +对切片的引用就是一个胖指针,携带者此切片的起始地址及其长度。 + +另一个胖指针是特型对象,即对实现了指定特型的值的引用。特型对象会携带一个值的地址和指向适用于该值的特型实现的指针。 + +## 生命周期 + +### 包含引用的结构体 + +对于一个包含引用的结构体,Rust 要求我们必须要写出它的生命周期 + +```rust +struct Person { + name: &str, +} +``` + +编译器会贴心的为我们提示需要修改的地方 + +```bash +error[E0106]: missing lifetime specifier + --> examples/reference.rs:2:11 + | +2 | name: &str, + | ^ expected named lifetime parameter + | +help: consider introducing a named lifetime parameter + | +1 ~ struct Person<'a> { +2 ~ name: &'a str, + | +``` + +### 不同的生命周期参数 + +当一个结构体只多个字段分别来自不同的生命周期的变量,而结构体本身只有一个生命周期时,编译器可能无法推断准确的引用。 + +```rust +struct Person<'a> { + pub name: &'a str, + pub age: &'a u32, +} + +fn main() { + let name = String::from("xfy"); + let n; + { + let age = 18u32; + let xfy = Person { + name: &name, + age: &age, + }; + n = xfy.name; + } + println!("{}", n); +} +``` + +```bash +error[E0597]: `age` does not live long enough + --> examples/reference.rs:13:18 + | +10 | let age = 18u32; + | --- binding `age` declared here +... +13 | age: &age, + | ^^^^ borrowed value does not live long enough +... +16 | } + | - `age` dropped here while still borrowed +17 | println!("{}", n); + | - borrow later used here +``` + +虽然代码本身不会创建任何悬空指针,但是编译器还是拒绝了这段代码。 + +- `Person` 的两个字段在生命周期的生命上具有相同的生命周期 `'a` 的引用,因此 Rust 必须要找到一个同时适合 `xfy.name` 和 `xfy.age` 的生命周期。 +- 赋值 `n = xfy.name` 就要求 `'a` 涵盖到变量 `n` 到生命周期。 +- 使用 `&age` 初始化 `xfy.age`,这就要求 `'a` 不能长于 `age` 的生命周期。 + +显然这些约束是不能同时满足的,所以就需要为结构体的不同字段声明使用不同的生命周期。 + +```rust +struct Person<'a, 'b> { + pub name: &'a str, + pub age: &'b u32, +} + +fn main() { + let name = String::from("xfy"); + let n; + { + let age = 18u32; + let xfy = Person { + name: &name, + age: &age, + }; + n = xfy.name; + } + println!("{}", n); +} +```