mirror of
https://github.com/DefectingCat/DefectingCat.github.io
synced 2025-07-15 08:41:37 +00:00
125 lines
4.4 KiB
Plaintext
125 lines
4.4 KiB
Plaintext
---
|
||
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);
|
||
}
|
||
```
|