当我在结构中使用可变引用而不是不可变引用时,为什么会出现生命周期错误? [英] Why do I get a lifetime error when I use a mutable reference in a struct instead of an immutable reference?

查看:190
本文介绍了当我在结构中使用可变引用而不是不可变引用时,为什么会出现生命周期错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此代码可以正常工作(游乐场):

This code works fine (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更改为可变引用时(

But when I change x to be a mutable reference instead (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 | |     }
  | |_____^

为什么会这样?据我所知,生存期没有任何变化:所有值/引用的生存时间与第一个代码段中的生存时间完全一样.

Why does that happen? As far as I see it, nothing about the lifetimes has changed: all values/references still live exactly as long as in the first code snippet.

推荐答案

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

Why does the Rust compiler reject this implementation of get? Because it allows:

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

The following is a perfectly reasonable main, assuming that get compiles:

fn main() {
    let mut x = 3;

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

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

但是,如果要编译get,就可以了:

Yet if get were to compile, this would be fine:

  • a不借用y,因为生命周期不同
  • b消费" y(从y.x移出),但之后我们不再重用
  • a does not borrow y because the lifetime is different
  • b "consumes" y (moving from y.x) but we do not reuse it after

所以一切都很好,只不过我们现在有一个&i32&mut i32 两者都指向x.

So everything is fine, except that we now have a &i32 and &mut i32 both pointing to x.

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

Note: to make it compile, you can use unsafe inside of get: unsafe { std::mem::transmute(&*self.x) }; scary, eh?

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

At the heart of the borrow-checking algorithm is the cornerstone on which Rust's memory safety is built:

别名异或性

Rust通过保证在您进行任何修改时,没有观察者在其中可能存在悬挂对象的引用,从而确保了内存的安全而不进行垃圾收集.

Rust achieves memory safety without garbage collection by guaranteeing that whenever you are modifying something, no observer can have a reference inside that something that could become dangling.

这反过来让我们解释:

  • &T作为别名参考;是Copy
  • &mut T作为唯一参考;它不是Copy,因为它会违反唯一性,但可以移动
  • &T as an aliasing reference; it is Copy
  • &mut T as a unique reference; it is NOT Copy, as it would violate uniqueness, but it can be moved

这种差异在这里救了我们

This difference saved us here.

由于不能复制&mut T,因此从&mut T&T(或&mut T)的唯一方法是执行重新借用:取消引用并引用结果.

Since &mut T cannot be copied, the only way to go from &mut T to &T (or &mut T) is to perform a re-borrowing: dereference and take a reference to the result.

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

This is done implicitly by the compiler. Doing it manually makes for a somewhat better error message:

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,所以两者之间没有任何关系!

Why cannot it infer a lifetime? Because lifetime of the re-borrow is limited by 'b but we are requiring a 'a and there's no relationship between the two!

顺便说一下,这就是使我们免于犯错误的原因,因为它确保了在结果存在时必须借用实例Foo (防止我们通过<使用可变引用c28>).

By the way, this is what is saving us from blunder here, because it ensures that the instance Foo must be borrowed while the result lives (preventing us to use a mutable reference via Foo::x).

遵循编译器提示,然后返回&'b i32可以正常工作...并阻止上述main编译:

Following the compiler hint, and returning &'b i32 works... and prevents the above main from compiling:

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编译没有问题:

However it lets the first main compile without issue:

fn main() {
    let mut x = 3;

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

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

打印3.

这篇关于当我在结构中使用可变引用而不是不可变引用时,为什么会出现生命周期错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆