【问题标题】:Why do I get "does not live long enough" in a return value?为什么我在返回值中得到“寿命不够长”?
【发布时间】:2019-05-04 07:24:05
【问题描述】:

在检查How do I box Arc and Mutexed variables? 时,我遇到了一个问题,即看起来正常的代码在从Mutex 构造返回值时生成“寿命不够长”错误。只需将 lock().unwrap() 从返回对象中拉出访问即可消除错误 - 但我想了解为什么 Rust 在这种情况下抱怨生命周期问题。

我能够将代码缩减为一个非常简单的复制器:第一个函数编译正常,第二个生成错误消息,它们几乎相同。

use std::sync::Mutex;

pub struct Response {
    resp: String,
}

pub fn get() -> Response {
    let body = Mutex::new("a".to_string());
    let x: std::sync::MutexGuard<_> = body.lock().unwrap();
    Response { resp: x.clone() }
}

pub fn get2() -> Response {
    let body = Mutex::new("a".to_string());
    Response {
        resp: body.lock().unwrap().clone(),
    }
}
error[E0597]: `body` does not live long enough
  --> src/lib.rs:16:15
   |
16 |         resp: body.lock().unwrap().clone(),
   |               ^^^^ borrowed value does not live long enough
17 |     }
18 | }
   | - `body` dropped here while still borrowed
   |
   = note: values in a scope are dropped in the opposite order they are created

【问题讨论】:

  • 奇怪!只拉出body.lock(),也会出现同样的错误。
  • 似乎它希望MutexGuardMutex 寿命更长(MutexGuard 借用Mutex)。不知道为什么!

标签: rust language-lawyer


【解决方案1】:

正如 Stargateur 的回答所指出的,原因是临时工的寿命。虽然 Rust 还没有完整的规范,但语言参考仍然非常好,至少给出了理解行为的提示。这是section about temporary lifetimes的相关部分:

[T]临时值的生命周期通常是

  • 最里面的封闭语句;块的尾部表达式被认为是包含该块的语句的一部分,或者
  • 如果在 if 的条件表达式或 while 表达式的循环条件表达式中创建临时变量,则为条件表达式或循环条件表达式。

在您的函数的第二个版本中,body.lock().unwrap() 最里面的封闭语句是返回表达式。上面的规范声明这个表达式是“被认为是包含块的语句的一部分”,在这种情况下实际上并不存在,但它仍然给出了正确的想法:函数体本地的所有变量都被删除 在返回表达式中的任何临时对象被删除之前,因此body 在借用body 的 MutexGuard 之前被删除。您找到的修复程序确保在 body 之前删除临时变量,因为局部变量的删除顺序与它们的创建顺序大致相反。

【讨论】:

  • 这表明另一种解决方案是使用return Response { resp: body.lock().unwrap().clone() };,(注意最后的; 意味着返回不再是块的尾部)。确实这编译OK。
【解决方案2】:

使用#![feature(nll)] 会给出一个精确的错误:

   |
19 |         resp: body.lock().unwrap().clone(),
   |               ^^^^----------------
   |               |
   |               borrowed value does not live long enough
   |               a temporary with access to the borrow is created here ...
20 |     }
21 | }
   | -
   | |
   | `body` dropped here while still borrowed
   | ... and the borrow might be used here, when that temporary is dropped and runs the `Drop`
   | code for type `std::sync::MutexGuard`
   |
   = note: The temporary is part of an expression at the end of a block. Consider forcing
   this temporary to be dropped sooner, before the block's local variables are dropped.
   For example, you could save the expression's value in a new local variable `x` and then make
   `x` be the expression at the end of the block.

BTW 相当长 ;) 这是因为临时是 drop after body,你也可以这样做:

pub fn get3() -> Response {
    let body = Mutex::new("a".to_string());
    let resp = body.lock().unwrap().clone();
    Response {
        resp,
    }
}

【讨论】:

  • 谢谢!与nll 的交叉检查总是让我无法理解!
猜你喜欢
  • 2018-12-20
  • 2016-01-22
  • 1970-01-01
  • 2016-03-31
  • 2015-02-28
  • 2020-12-17
  • 2015-05-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多