rust - 当我在结构中使用可变引用而不是不可变引用时,为什么会出现生命周期错误?

标签 rust lifetime

这段代码工作正常(Playground):

struct F<'a> {
    x: &'a i32,
}

impl<'a> F<'a> {
    fn get<'b>(&'b self) -> &'a i32 {
        self.x
    }
}

fn main() {
    let x = 3;
    let y = F { x: &x };
    let z = y.get();
}

但是当我将 x 更改为可变引用时 (Playground):

struct Foo<'a> {
    x: &'a mut i32,  // <-- `mut` added
}

impl<'a> Foo<'a> {
    fn get(&self) -> &'a i32 {
        self.x
    }
}

fn main() {
    let mut x = 3;              // <-- `mut` added
    let y = Foo { x: &mut x };  // <-- `mut` added
    let z = y.get();
}

我收到这个错误:

error[E0312]: lifetime of reference outlives lifetime of borrowed content...
 --> src/main.rs:7:9
  |
7 |         self.x
  |         ^^^^^^
  |
note: ...the reference is valid for the lifetime 'a as defined on the impl at 5:6...
 --> src/main.rs:5:6
  |
5 | impl<'a> Foo<'a> {
  |      ^^
note: ...but the borrowed content is only valid for the anonymous lifetime #1 defined on the method body at 6:5
 --> src/main.rs:6:5
  |
6 | /     fn get(&self) -> &'a i32 {
7 | |         self.x
8 | |     }
  | |_____^

为什么会这样?据我所知,生命周期没有任何变化:所有值/引用仍然与第一个代码片段中一样长。

最佳答案

为什么 Rust 编译器拒绝这种 get 的实现?因为它允许:

以下是一个完全合理的 main,假设 get 编译:

fn main() {
    let mut x = 3;

    let y = Foo { x: &mut x };
    let a = y.get();
    let b = y.x;

    println!("{} {}", a, b);
}

然而,如果 get 被编译,这会很好:

  • a 没有借用y 因为生命周期不同
  • b“消耗”y(从 y.x 移动)但我们不会在之后重用它

所以一切都很好,除了我们现在有一个 &i32&mut i32 both 指向 x .

注意:要使其编译,您可以在 get 中使用 unsafe: unsafe { std::mem::transmute(&* self.x) };可怕吧?


借用检查算法的核心是构建 Rust 内存安全性的基石:

Aliasing XOR Mutability

Rust 通过保证无论何时修改某些内容,任何观察者都无法在可能成为悬空的内容中获得引用,从而在没有垃圾收集的情况下实现内存安全。

反过来,这让我们解释:

  • &T 作为别名引用;它是复制
  • &mut T 作为唯一引用;它不是复制,因为它会违反唯一性,但它可以移动

这种差异拯救了我们。

由于 &mut T 无法复制,唯一的方法是从 &mut T&T (或 &mut T) 是执行重新借用:解引用并引用结果。

这是由编译器隐式完成的。手动执行此操作会产生更好的错误消息:

fn get<'b>(&'b self) -> &'a i32 {
    &*self.x
}
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
 --> <anon>:7:9
  |
7 |         &*self.x
  |         ^^^^^^^^
  |
help: consider using an explicit lifetime parameter as shown: fn get(&'a self) -> &'a i32
 --> <anon>:6:5
  |
6 |     fn get<'b>(&'b self) -> &'a i32 {
  |     ^

为什么它不能推断一生?因为重新借用的生命周期受 'b 限制,但我们需要一个 'a 并且两者之间没有关系!

顺便说一句,这就是避免我们犯错误的原因,因为它确保实例 Foo 必须 在结果存在时被借用(防止我们使用通过 Foo::x 的可变引用)。

按照编译器提示,返回 &'b i32 有效...并阻止上面的 main 编译:

impl<'a> Foo<'a> {
    fn get<'b>(&'b self) -> &'b i32 {
        &*self.x
    }
}

fn main() {
    let mut x = 3;

    let y = Foo { x: &mut x };
    let a = y.get();
    let b = y.x;

    println!("{} {}", a, b);
}
error[E0505]: cannot move out of `y.x` because it is borrowed
  --> <anon>:16:9
   |
15 |     let a = y.get();
   |             - borrow of `y` occurs here
16 |     let b = y.x;
   |         ^ move out of `y.x` occurs here

但是它让第一个 main 编译没有问题:

fn main() {
    let mut x = 3;

    let y = Foo { x: &mut x };
    let z = y.get();

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

打印 3

关于rust - 当我在结构中使用可变引用而不是不可变引用时,为什么会出现生命周期错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54586997/

相关文章:

rust - 为什么只能用特征指定类型限制?

Rust 迭代器类似于 chunks()

rust - 为什么我需要这些生命周期而不需要其他生命周期,如何知道正确的签名以及如何识别需要生命周期的参数?

rust - 存储可变引用和生命周期

asynchronous - 如何在异步函数中使用谓词?

rust - “静态作为函数约束”是什么意思?

hashmap - 如何为我自己的结构实现 Eq 和 Hash 以将它们用作 HashMap 键?

rust - 为什么不能在同一结构中存储值和对该值的引用?

rust - 变量生命周期不够长 : matching Option type

rust - Rust提示我正在编写的方法存在生命周期