返回介绍

4.3 生命周期

发布于 2024-10-13 11:25:29 字数 3037 浏览 0 评论 0 收藏 0

生命周期(lifetime)是编译器比对资源所有者(owner)和借用者(borrow)存活时间的工具。根本目的,杜绝类似悬垂指针的无效引用。

多数时候,编译器能自主完成生命周期检查工作。偶尔也需手工 标记 (annotation),毕竟静态分析有许多不足和限制。当然,这只是对借用检查器(borrow checker)进行提示,并不会生成额外运行期指令,也不会对原对象有任何影响。

和 GC 运行期遍历不同,检查器要在编译期确定资源何时何处释放,就需要收集额外的信息。

&i32      // 引用。
&'a i32     // 带有生命周期标记的引用。
&'a mut i32
// implicit
fn foo(x: &i32) {
}

// explicit
fn bar<'a>(x: &'a i32) {
}

下面的示例函数从逻辑上讲,没有问题。但对检查器来说却有麻烦,因为返回值生命周期和参数有关。调用时,会因参数导致安全问题。

fn longest(x: &str, y: &str) -> &str {
  if x.len() > y.len() { x } else { y }
}
fn main() {
  let x = String::from("xxx");
  let r: &str;

  {
    let y = String::from("yyyy");
    r = longest(x.as_str(), y.as_str());
  }

  println!("{}", r);
}

如果 x.len > y.len ,逻辑正确。反过来, r 去引用生命周期更短的 y ,就有安全问题。这样就存在分歧,检查器无法作出决定。只能提示我们干预,进行标注。

error[E0106]: missing lifetime specifier

  |
  | fn longest(x: &str, y: &str) -> &str {
  |         ----   ----   ^ expected named lifetime parameter
  |

标记语法和泛型类似。但要注意,生命周期标记的确切含义是:不能超出 !

  • 多个相同标记的生命周期,取周期最短的作为决策依据。
  • 返回值和参数用相同标记,表示不能超过参数生命周期。

标记后,检查器已经能指出具体问题所在。

// 返回值生命周期不能超过 x、y 里短的那个。
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {   
  if x.len() > y.len() { x } else { y }
}

fn main() {
  let x = String::from("xxx");
  let r: &str;

  {
    let y = String::from("yyyy");
    r = longest(x.as_str(), y.as_str());
                ^ borrowed value does not live long enough
  }
  - `y` dropped here while still borrowed

  println!("{}", r);
           - borrow later used here
}

约束

'b:'a 语法声明两个生命周期长短关系: b >= ab 至少和 a 活得一样久。

类似的还有 T:'a ,判断类型生命周期。

依照约束,返回值生命周期不能超过参数 x ,更不能超过 y

fn longest<'a, 'b:'a>(x: &'a str, y: &'b str) -> &'a str {
  if x.len() > y.len() { x } else { y }
}

调用时,编译器并不要求 实参 y 实际生命周期大过 x

  • y >= x :符合 'b:'a 约束。
  • x >= y :按约束要求, a 取和 b 重叠部分(片段)。
fn main() {
  let x = String::from("xxx");          //  . . . . .
                          //      .
  {                       //      . 
    let y = String::from("yyyy");       //  ---+  --+
    let r = longest(x.as_str(), y.as_str());  //  'b |  | 'a
    println!("{}", r);            //   |  |
  }                       //  ---+  --+
}                         //  . . . . .

静态

类似 C 静态局部变量,拥有进程级别的生命周期。

.data segment

fn main() {
  static FOO: i32 = 5;
  let x: &'static i32 = &FOO;
}

字符串字面量也有静态生命周期。

let x: &'static str = "Hello, world.";

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文