从作为参数传递给函数的引用返回内部引用时的生命周期处理

尽管生命周期’a’和’b彼此独立,但下面的代码编译得很好的原因是什么?

struct Foo<'a> {
    i: &'a i32
}

fn func<'a, 'b>(x: &'a Foo<'b>) -> &'b i32 {
    x.i
}

fn main() {}

如果我在Foo中使引用i可变,则会出现以下错误.

5 | fn func<'a, 'b>(x: &'a Foo<'b>) -> &'b i32 {
  |                    -----------     -------
  |                    |
  |                    this parameter and the return type are declared with different lifetimes...
6 |     x.i
  |     ^^^ ...but data from `x` is returned here

它给出上述错误的原因是什么?它是否认为它是对可变引用的所有权并且它看到某些东西(来自Foo)被取出(具有独立的生命周期),这是不可能的,因此错误?

这段代码(我认为会通过)也会失败:

struct Foo<'a> {
    i: &'a mut i32
}

fn func<'a, 'b: 'a>(x: &'a Foo<'b>) -> &'b i32 {
    x.i
}

fn main() {}

失败并出错:

 error[E0623]: lifetime mismatch
 --> src/main.rs:6:5
  |
5 | fn func<'a, 'b: 'a>(x: &'a Foo<'b>) -> &'b i32 {
  |                        -----------
  |                        |
  |                        these two types are declared with different lifetimes...
6 |     x.i
  |     ^^^ ...but data from `x` flows into `x` here

但是这个通过了:

struct Foo<'a> {
    i: &'a mut i32
}

fn func<'a: 'b, 'b>(x: &'a Foo<'b>) -> &'b i32 {
    x.i
}

fn main() {}

这对我来说似乎有点违反直觉.在这里,外部寿命(‘a)可能比内部寿命(‘b)更长.为什么这不是错误?

最佳答案

What is the reason it gives the above error?. Does it consider it’s
ownership over mutable reference and it sees that something (from Foo)
is being taken out (with an independent lifetime), which is not
possible, hence the error ?

可变借款确实不能从Foo中移出,因为可变借款不是复制.它隐含地不可逆转地重新借贷:

fn func <'a, 'b> (x:&'a Foo<'b>) -> &'b i32 {
    &*x.i
}

但是,这不是问题的根源.首先,这里是func的四个版本的摘要(struct Foo在我的解释中没有相关性):

版本1(编译):

fn func<'a, 'b>(x: &'a &'b i32) -> &'b i32 {
    x
}

版本2(无法编译):

fn func<'a, 'b>(x: &'a &'b mut i32) -> &'b i32 {
    x
}

版本3(无法编译):

fn func<'a, 'b: 'a>(x: &'a &'b mut i32) -> &'b i32 {
    x
}

版本4(编译):

fn func<'a: 'b, 'b>(x: &'a &'b mut i32) -> &'b i32 {
    x
}

版本2和3失败,因为它们违反了无别名规则,该规则禁止同时具有可变引用和对资源的不可变引用.在两个版本中’b可能严格超过’a.因此,&’b mut i32和&’b i32可以共存.版本1编译,因为别名规则允许同时对资源进行多次不可变引用.因此,&’i i32可以合法地与anothor&’i32共存.

乍一看,看起来版本4应该失败,因为再次有一个可变的借位和相同生命周期的不可变借用.与版本2和版本3的不同之处在于,由于要求’a:’b,这个时间’至少与’b一样长,这意味着’b可能不会严格超过’a.只要生命周期’持续,引用的i32不能第二次可变地借用(可变引用不是复制) – i32已经可变地借用了func调用.

这是一个示例,演示版本2和3如何导致未定义的行为:

fn func<'a, 'b: 'a>(x: &'a &'b mut String) -> &'b str {
    unsafe { std::mem::transmute(&**x as &str) } // force compilation
}

fn main() {
    let mut s = String::from("s");
    let mutref_s = &mut s;
    let ref_s = {
        let ref_mutref_s = &mutref_s;
        func(ref_mutref_s)
    };
    // use the mutable reference to invalidate the string slice       
    mutref_s.clear();
    mutref_s.shrink_to_fit();
    // use the invalidated string slice
    println!("{:?}", ref_s);
}

使用版本4交换版本3显示了在这种情况下仍然是活动的外部不可变借用如何阻止第二次可变借用.新的要求’a:’b将被扩展为等于终身’b,迫使外部不可变借入的生命周期’a:

error[E0502]: cannot borrow `*mutref_s` as mutable because `mutref_s` is also borrowed as immutable
  --> src/main.rs:20:5
   |
17 |         let ref_mutref_s = &mutref_s;
   |                             -------- immutable borrow occurs here
...
20 |     mutref_s.clear();
   |     ^^^^^^^^ mutable borrow occurs here
...
23 | }
   | - immutable borrow ends here

转载注明原文:从作为参数传递给函数的引用返回内部引用时的生命周期处理 - 代码日志