rust-by-practice/zh-CN/src/lifetime/advance.md

286 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 深入生命周期
## 特征约束
就像泛型类型可以有约束一样,生命周期也可以有约束 ,如下所示:
- `T: 'a`,所有引用在 `T` 必须超过生命周期 `'a`
- `T: Trait + 'a`: `T` 必须实现特征 `Trait` 并且所有引用在 `T` 必须超过生命周期 `'a`
**示例**
```rust,editable
use std::fmt::Debug; // 特征约束使用
#[derive(Debug)]
struct Ref<'a, T: 'a>(&'a T);
// `Ref` 包含对泛型类型 `T` 的引用,该泛型类型具有
// 未知的生命周期 `'a`. `T` 是约定任何
// 引用在 `T` 必须大于 `'a` 。此外,在生命周期
// 里 `Ref` 不能超过 `'a`
// 使用 `Debug` 特征打印的通用函数。
fn print<T>(t: T) where
T: Debug {
println!("`print`: t is {:?}", t);
}
// 这里引用 `T` 使用 where `T` 实现
// `Debug` 和所有引用 `T` 都要比 `'a`
// 此外,`'a`必须要比函数声明周期长
fn print_ref<'a, T>(t: &'a T) where
T: Debug + 'a {
println!("`print_ref`: t is {:?}", t);
}
fn main() {
let x = 7;
let ref_x = Ref(&x);
print_ref(&ref_x);
print(ref_x);
}
```
1. 🌟
```rust,editable
/* 使用生命周期注释结构体
1. `r` 和 `s` 必须是不同生命周期
2. `s` 的生命周期需要大于 'r'
*/
struct DoubleRef<T> {
r: &T,
s: &T
}
fn main() {
println!("Success!")
}
```
2. 🌟🌟
```rust,editable
/* 添加类型约束使下面代码正常运行 */
struct ImportantExcerpt<'a> {
part: &'a str,
}
impl<'a, 'b> ImportantExcerpt<'a> {
fn announce_and_return_part(&'a self, announcement: &'b str) -> &'b str {
println!("Attention please: {}", announcement);
self.part
}
}
fn main() {
println!("Success!")
}
```
3. 🌟🌟
```rust,editable
/* 添加类型约束使下面代码正常运行 */
fn f<'a, 'b>(x: &'a i32, mut y: &'b i32) {
y = x;
let r: &'b &'a i32 = &&0;
}
fn main() {
println!("Success!")
}
```
## HRTB更高等级特征约束(Higher-ranked trait bounds)
类型约束可能在生命周期中排名更高。这些约束指定了一个约束对于所有生命周期都为真。例如,诸如此类的约束 `for<'a> &'a T: PartialEq<i32>` 需要如下实现:
```rust
impl<'a> PartialEq<i32> for &'a T {
// ...
}
```
然后可以用于将一个 `&'a T` 与任何生命周期进行比较 `i32`
这里只能使用更高级别的约束,因为引用的生命周期比函数上任何可能的生命周期参数都短。
4. 🌟🌟🌟
```rust
/* 添加 HRTB 使下面代码正常运行! */
fn call_on_ref_zero<'a, F>(f: F) where F: Fn(&'a i32) {
let zero = 0;
f(&zero);
}
fn main() {
println!("Success!")
}
```
## NLL非词汇生命周期(Non-Lexical Lifetime)
在解释 NLL 之前,我们先看一段代码:
```rust
fn main() {
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;
println!("{} and {}", r1, r2);
let r3 = &mut s;
println!("{}", r3);
}
```
根据我们目前的知识,这段代码会因为违反 Rust 中的借用规则而导致错误。
但是,如果您执行 `cargo run` ,那么一切都没问题,那么这里发生了什么?
编译器在作用域结束之前判断不再使用引用的能力称为 **非词法生命周期**(简称 **NLL** )。
有了这种能力,编译器就知道最后一次使用引用是什么时候,并根据这些知识优化借用规则。
```rust
let mut u = 0i32;
let mut v = 1i32;
let mut w = 2i32;
// lifetime of `a` = α β γ
let mut a = &mut u; // --+ α. lifetime of `&mut u` --+ lexical "lifetime" of `&mut u`,`&mut u`, `&mut w` and `a`
use(a); // | |
*a = 3; // <-----------------+ |
... // |
a = &mut v; // --+ β. lifetime of `&mut v` |
use(a); // | |
*a = 4; // <-----------------+ |
... // |
a = &mut w; // --+ γ. lifetime of `&mut w` |
use(a); // | |
*a = 5; // <-----------------+ <--------------------------+
```
## 再借用
学习了 NLL 之后,我们现在可以很容易地理解再借用了。
**示例**
```rust
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
impl Point {
fn move_to(&mut self, x: i32, y: i32) {
self.x = x;
self.y = y;
}
}
fn main() {
let mut p = Point { x: 0, y: 0 };
let r = &mut p;
// 这里是再借用
let rr: &Point = &*r;
println!("{:?}", rr); // 这里结束再借用
// 再借用结束,现在我们可以继续使用 `r`
r.move_to(10, 10);
println!("{:?}", r);
}
```
5. 🌟🌟
```rust,editable
/* 通过重新排序一些代码使下面代码正常运行 */
fn main() {
let mut data = 10;
let ref1 = &mut data;
let ref2 = &mut *ref1;
*ref1 += 1;
*ref2 += 2;
println!("{}", data);
}
```
## 未约束的生命周期
在 [Nomicon - Unbounded Lifetimes](https://doc.rust-lang.org/nomicon/unbounded-lifetimes.html) 中查看更多信息。
## 更多省略规则
```rust
impl<'a> Reader for BufReader<'a> {
// 'a 在以下方法中不使用
}
// 可以写为:
impl Reader for BufReader<'_> {
}
```
```rust
// Rust 2015
struct Ref<'a, T: 'a> {
field: &'a T
}
// Rust 2018
struct Ref<'a, T> {
field: &'a T
}
```
## 艰难的练习
6. 🌟🌟🌟🌟
```rust
/* 使下面代码正常运行 */
struct Interface<'a> {
manager: &'a mut Manager<'a>
}
impl<'a> Interface<'a> {
pub fn noop(self) {
println!("interface consumed");
}
}
struct Manager<'a> {
text: &'a str
}
struct List<'a> {
manager: Manager<'a>,
}
impl<'a> List<'a> {
pub fn get_interface(&'a mut self) -> Interface {
Interface {
manager: &mut self.manager
}
}
}
fn main() {
let mut list = List {
manager: Manager {
text: "hello"
}
};
list.get_interface().noop();
println!("Interface should be dropped here and the borrow released");
use_list(&list);
}
fn use_list(list: &List) {
println!("{}", list.manager.text);
}
```