繁体   English   中英

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

[英]Why do I get a lifetime error when I use a mutable reference in a struct instead of an immutable reference?

此代码工作正常( 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 (从yx移动)但我们不会在之后重用它

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

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


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

别名异或可变性

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

反过来,这让我们解释:

  • &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 `yx` because it is borrowed --> <anon>:16:9 | 15 | let a = y.get(); | - borrow of `y` occurs here 16 | let b = yx; | ^ move out of `yx` occurs here

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

fn main() {
    let mut x = 3;

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

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

打印3

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM