Rust抱怨释放了可变借位而不是不可变借位

nxowjjhe  于 2022-12-19  发布在  其他
关注(0)|答案(1)|浏览(139)

下面的两个rust函数除了一个返回一个不可变的引用,另一个返回一个可变的引用外,其他都是相同的。由于这两个函数都不涉及多次借用,所以我看不出这两个函数的工作原理有什么不同。然而,具有可变引用的函数会导致编译错误,而具有不可变引用的函数则不会:

// This complies with no problems
fn foo<'a>() {
    let _: &'a () = &();
}

// This does not compile (see error below)
fn foo_mut<'a>() {
    let _: &'a mut () = &mut ();
}
error[E0716]: temporary value dropped while borrowed
  --> src/main.rs:14:30
   |
13 | fn foo_mut<'a>() {
   |            -- lifetime `'a` defined here
14 |     let _: &'a mut () = &mut ();
   |            ----------        ^^ creates a temporary which is freed while still in use
   |            |
   |            type annotation requires that borrow lasts for `'a`
15 | }
   |  - temporary value is freed at the end of this statement

For more information about this error, try `rustc --explain E0716`.
error: could not compile `playground` due to previous error

当没有显式生存期时,代码也没有编译问题,这也可能是相关的:

// This also compiles with no problem
fn foo_mut_without_lifetime() {
    let _: &mut () = &mut ();
}

看起来唯一的问题是试图存储一个具有生存期的可变引用,而不可变引用和没有显式生存期的引用都没有问题。为什么会发生这种情况,我该如何解决它?

jpfvwuh4

jpfvwuh41#

注意,这里的()或泛型生存期没有什么特别之处。

fn allowed() -> &'static i32 {
    let x = &3;
    let y: &'static i32 = x;
    y
}

而这并不:

fn not_allowed() -> &'static mut i32 {
    let x = &mut 3;
    let y: &'static mut i32 = x;
    y
}

那么为什么允许不可变引用呢?

当你引用一个值的时候,Rust会根据这个值将要死的地方来推断它的生命周期,下面是一个例子:

let y;
{
    let x = 3;
    y = &x;
    println!("{y}"); // works fine, `y` is still alive
} // `x` will get dropped at the end of this block
println!("{y}"); // fails to compile, the lifetime of `y` has expired (since `x` has died)

由于x在块的末尾死亡,Rust知道y引用的生存期应该也只延长到块的末尾,因此,在x死亡之后,它会阻止你使用它。
这看起来很明显。但是请花点时间思考一下。在下面的代码中:

let x;
{ // block A
x = &3;
}

x的推断生存期是多少?你可能会忍不住说“和块A一样”。但这实际上是不正确的。为什么?因为Rust比这聪明一点。它知道3是一个 * 常量 *,因此Rust可以将3放入最终可执行文件的常量表中。而且由于常量表的持续时间与最终程序的生命周期一样长,**Rust可以推断表达式&3的生命周期为'static然后一切都很好,因为&'static可以根据需要强制转换为任何其他生命周期!
Rust在常量和临时变量之间划出了一条明确的界限,使用常量表达式的好处之一是
对任何常量进行不可变的引用都会产生一个'static生存期。**临时变量则不是这样,下面的代码将无法编译:

fn f() -> &'static String {
    let x = &String::new();
    let y: &'static String = x;
    y
}

这是因为对于临时变量,Rust不能把它们放在可执行文件的常量表中,因为它们必须按需计算,因此与它们所在的作用域共享相同的生命周期。

好的,这很好,但是为什么不允许常量的 mutable 引用为'static呢?

允许这样做有两个问题:
1.在一些架构上,常量表是不能修改的。WASM和一些嵌入式架构,以及所有哈佛架构的机器都是如此。提供&mut引用完全是无稽之谈,因为它们是不可变的。而且这种基本的借位检查规则在不同的平台之间应该没有区别。

  1. &'static mut引用是危险的,因为它实际上是一个全局变量。

相关问题