为什么我得到了一生的错误,当我在一个结构,而不是一个一成不变的参考用可变引用?

问题描述 投票:2回答:1

此代码工作正常(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 lifetime
1个回答
5
投票

为什么锈编译器拒绝这个实施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都指向x

注:既要编译,你可以使用unsafe get内:unsafe { std::mem::transmute(&*self.x) };可怕的,不是吗?


在借检查算法的心脏是在其锈病的存储安全建立的基石:

别名XOR可变性

锈病保证,只要你正在修改的东西,没有观察者可以有一些可能成为里面晃来晃去的参考实现存储安全无垃圾收集。

这一点,在转弯,让我们解释:

  • &T作为混叠参考;它是Copy
  • &mut T作为唯一的参考;它不是Copy,因为这将违反唯一性,但它可以移动

这种差异在这里救了我们。

由于&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

© www.soinside.com 2019 - 2024. All rights reserved.