为什么我的返回值会出现“does not live long enough”的错误?

14

在检查如何对Arc和Mutex变量进行打包时,我遇到了一个问题:看起来OK的代码在构建Mutex的返回值时会生成“does not live long enough”错误。只需将 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() 拉出来,也会出现相同的错误。 - vikram2784
似乎希望MutexGuard的生命周期比Mutex更长(MutexGuard借用了Mutex)。不太清楚为什么! - vikram2784
2个回答

7
正如Stargateur的回答所指出的,这是由于临时值的生命周期。虽然Rust还没有完整的规范,但语言参考仍然非常好,并且至少给出了一些提示以理解这种行为。这里是关于临时生命周期的相关部分:
“[T]he lifetime of temporary values is typically the innermost enclosing statement; the tail expression of a block is considered part of the statement that encloses the block, or the condition expression or the loop conditional expression if the temporary is created in the condition expression of an if or in the loop conditional expression of a while expression.”
在你的函数的第二个版本中,body.lock().unwrap()的最内层封闭语句是返回表达式。上述规范说明该表达式被视为“包含块的语句的一部分”,但在这种情况下并不存在这样的块,但它确实提供了正确的想法:在返回表达式中的任何临时变量被丢弃之前,函数主体中的所有局部变量都会被丢弃,因此body将在借用body的MutexGuard之前被丢弃。你找到的修复方法确保在body之前丢弃临时变量,因为本地变量的丢弃大致按照它们的创建顺序的相反顺序进行。

3
这表明另一种解决方案是使用 return Response { resp: body.lock().unwrap().clone() };,(请注意最后的 ; 意味着返回不再是块的尾部)。实际上,这可以编译通过。 - Michael Anderson

5

使用 #![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.

顺便提一下,这段代码比较长。因为临时变量在 body 后被清除了,你也可以这样写:

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

谢谢!使用“nll”进行交叉检查总是让我困扰! - vikram2784

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接