summaryrefslogtreecommitdiff
path: root/source/_posts/Rust-闭包-lifetime-may-not-live-long-enough-问题.md
blob: 71ffb83f5943a08bb26bc2d56982305ba956365c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
---
title: Rust 闭包 lifetime may not live long enough 问题
date: 2023-05-24 18:04:23
tags: [Technique]
---

代码:
```rust
...
    fn handlers(self) -> crate::server::request::Handlers {
        vec![(
            "/tree",
            routing::get(move || async {
                (
                    StatusCode::OK,
                    Json(json!(self.clone().tree(self.clone().root))),
                )
            }),
        )]
    }
...
```

编译错误:
```text
error: lifetime may not live long enough
  --> src/storage/filesystem/mod.rs:45:34
   |
45 |               routing::get(move || async {
   |  __________________________-------_^
   | |                          |     |
   | |                          |     return type of closure `{async block@src/storage/filesystem/mod.rs:45:34: 50:14}` contains a lifetime `'2`
   | |                          lifetime `'1` represents this closure's body
46 | |                 (
47 | |                     StatusCode::OK,
48 | |                     Json(json!(self.clone().tree(self.clone().root))),
49 | |                 )
50 | |             }),
   | |_____________^ returning this value requires that `'1` must outlive `'2`
   |
   = note: closure implements `Fn`, so references to captured variables can't escape the closure
```

这是因为 `handlers` 里面的闭包捕获了一个引用,并且尝试返回一个包含该引用的值导致的。

细说就是:闭包内部使用了 `self.clone()` 来获取一个新的实例,然后在异步块中返回一个 JSON 对象,这个 JSON 对象依赖于 `self.tree()` 的结果。因为闭包捕获了 `self` 的引用,所以它必须保证 `self` 在闭包执行完毕后仍然有效。

解决这个问题的思路是:确保闭包中的所有引用都在闭包执行完毕之前就不再被使用。
就是说,要将闭包的作用域限制在一个更短的生命周期内,或者使用其他方式来避免闭包捕获长期存在的引用:

```rust

...
    fn handlers(self) -> crate::server::request::Handlers {
        let tree = json!(self.clone().tree(self.clone().root));
        vec![(
            "/tree",
            routing::get(move || async { (StatusCode::OK, Json(tree)) }),
        )]
    }
...
```