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