在方法中链接self和引用的生命周期

12

我有 这段代码

#[derive(Debug)]
struct Foo<'a> {
    x: &'a i32,
}

impl<'a> Foo<'a> {
    fn set(&mut self, r: &'a i32) {
        self.x = r;
    }
}

fn main() {
    let v = 5;
    let w = 7;
    let mut f = Foo { x: &v };

    println!("f is {:?}", f);

    f.set(&w);

    println!("now f is {:?}", f);
}

根据我的理解,在第一次借用v的值时,结构体声明中的通用生命周期参数'a会被填充为v值的生命周期。这意味着生成的Foo对象不能比这个生命周期更长,或者说v的值必须至少和Foo对象一样长。

在调用set方法时,impl块上的生命周期参数会被使用,并且w值的生命周期会被填充为方法签名中的'a参数。编译器会为&mut self分配另一个生命周期,即fFoo对象)的生命周期。如果我在main函数中交换了wf的绑定顺序,这将导致错误。

我想知道如果我给&mut self引用注释相同的生命周期参数'a,跟set方法中的r一样,会发生什么:

impl<'a> Foo<'a> {
    fn set(&'a mut self, r: &'a i32) {
        self.x = r;
    }
}

这会导致以下错误:

error[E0502]: cannot borrow `f` as immutable because it is also borrowed as mutable
  --> src/main.rs:21:31
   |
19 |     f.set(&w);
   |     - mutable borrow occurs here
20 | 
21 |     println!("now f is {:?}", f);
   |                               ^ immutable borrow occurs here
22 | }
   | - mutable borrow ends here

与上面的示例不同,f在第二个println!被调用时仍然被认为是可变地借用的,因此它不能同时作为不可变的借用。

这是如何发生的呢?

在第一个示例中,如果没有留下生命周期注释,编译器会为我填写一个&mut self的生命周期注释。这是由于生命周期省略规则引起的。但是,在第二个示例中,通过将其明确设置为'a,我将f的值和w的值的生命周期联系在一起。

f是否被认为是自身被借用了吗?

如果是这样,这种借用的范围是什么?是min(f的生命周期,w的生命周期) -> f的生命周期吗?

我想我还没有完全理解函数调用中的&mut self借用。我的意思是,函数返回了,但f仍然被认为被借用了。

我正在努力理解生命周期。我主要寻求关于概念理解的纠正意见。我非常感谢每一点建议和进一步的解释。

1个回答

7
在调用方法set时,impl块上的lifetime参数被使用,并且将w的值的lifetime填充到了方法签名中的'a中。但是,lifetime参数'a的值在创建Foo结构体时就已经固定了,因为它是其类型的一部分,所以不会改变。
在您的情况下,编译器实际上选择了一个与vw的lifetime都兼容的值作为'a。如果这是不可能的,编译器会提示错误,例如以下示例:
fn main() {
    let v = 5;
    let mut f = Foo { x: &v };

    println!("f is {:?}", f);
    let w = 7;
    f.set(&w);

    println!("now f is {:?}", f);
}

输出如下:

error[E0597]: `w` does not live long enough
  --> src/main.rs:21:1
   |
18 |     f.set(&w);
   |            - borrow occurs here
...
21 | }
   | ^ `w` dropped here while still borrowed
   |
   = note: values in a scope are dropped in the opposite order they are created

正因为 v 强加的 'a 生命周期与 w 的较短生命周期不兼容,所以才出现上述错误。

在第二个示例中,将 self 的生命周期强制设置为 'a 后,您还将可变借用绑定到了 'a 生命周期,这样一来,当所有 'a 生命周期的项都超出范围时(即 vw),借用就会结束。


有趣。因此,在第一个示例中,如果值在创建时固定,则编译器还考虑方法调用中的赋值来确定'a。但是'a仍然必须至少与f的生命周期一样长。 - jtepe
@JonasTepe 是的,就是这样。 - Levans
从那个例子中,第二个例子现在也变得非常清晰了。非常感谢。 - jtepe
1
@Levans,现在这个例子可以编译了。 - rethab

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接