为什么我会在返回值中得到"活得不够长"?

Mic*_*son 7 language-lawyer rust

在检查如何选中Arc和Mutexed变量?我遇到了一个问题,在这个问题中,看起来正常的代码在从a构造返回值时会产生"不够长时间"的错误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(),
    }
}
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)

Sve*_*ach 7

正如 Stargateur 的回答所指出的,其原因是临时变量的生命周期。虽然 Rust 还没有完整的规范,但语言参考仍然相当不错,至少给出了理解其行为的提示。以下是有关临时生命周期部分的相关部分:

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

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

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

  • 这表明替代解决方案是使用“return Response { resp: body.lock().unwrap().clone() };”,(注意最后的“;”意味着返回不是块的尾部不再)。确实可以编译。 (3认同)

Sta*_*eur 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.
Run Code Online (Sandbox Code Playgroud)

顺便说一句,相当长;)这是因为临时文件是在正文之后删除的,您也可以这样做:

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