繁体   English   中英

生命周期中捕获的引用

[英]Captured references in lifetimes

在(人为简化的)我的项目中,我有一个结构ContainsData ,它只能通过句柄HoldsRef<'a>修改,它包含对ContainsData中某个字段的引用。 此外,在使用句柄后,必须调用一些cleanup方法。

struct ContainsData(i64);
impl ContainsData {
    fn cleanup(&mut self) {
        self.0 = self.0.abs();
    }
}

struct HoldsRef<'a>(&'a mut i64);
impl<'a> HoldsRef<'a> {
    fn set(&mut self, value: &'a i64) {
        *self.0 += value; 
    }
}

// A typical interaction with "ContainsData"
fn main() {
    let mut container = ContainsData(5);
    let x = 32;

    let mut handle = HoldsRef(&mut container.0);     // _
    handle.set(&x);                                  // |  A    
    handle.set(&31);                                 // |
    container.cleanup()                              // v
}

我想用对ContainsData的单个方法调用替换 A 。 对句柄的所有修改都将在闭包的上下文中完成,如下所示:

impl ContainsData {
    fn modify(&mut self, continuation : impl Fn(HoldsRef)) {
        let handle = HoldsRef(&mut self.0); 
        continuation(handle);
        self.cleanup()
    }
}

fn main2() {
    let mut container = ContainsData(5);
    let x = 32;

    container.modify(|mut handle| {
        handle.set(&x);
        handle.set(&32);
    });
}

这不编译:

error[E0597]: `x` does not live long enough
  --> src/main.rs:56:21
   |
55 |     container.modify(|mut handle| {
   |                      ------------ value captured here
56 |         handle.set(&x);
   |         ------------^-
   |         |           |
   |         |           borrowed value does not live long enough
   |         argument requires that `x` is borrowed for `'static`
...
59 | }
   | - `x` dropped here while still borrowed

第一个问题:为什么 Rust 认为x是为'static借来的? 我假设它与Fn(HoldsRef)的省略规则有关,但我不太明白在这种情况下规则说的是什么。

第二个问题:有没有办法更改modify的签名或稍微更改代码以便能够用单个方法调用替换 A ? 我曾尝试以各种方式注释生命周期,但都无济于事。

fn modify<'a>(&mut self, continuation : impl Fn(HoldsRef<'a>))
// cannot infer an appropriate lifetime (b/c, I guess, the handle has a lifetime strictly smaller than the function)

更广泛的上下文: HoldsRef<'a>实际上是wgpu::RenderPass<'a> 我没有更改方法set签名的选项。

让我们了解这里发生了什么。

闭包中的生命周期( impl Fn(&T)impl Fn(T<'_>) ,与您的impl Fn(HoldsRef)相同),不要像普通的省略生命周期那样去糖化通用生命周期(其中fn foo(_: &T)脱糖成fn foo<'a>(_: &'a T) ); 相反,它们被脱糖到更高等级的 trait bounds中。

也就是说, impl Fn(&T)脱糖为impl for<'a> Fn(&'a T) ,同样, impl Fn(HoldsRef)脱糖为impl for<'a> Fn(HoldsRef<'a>) . 另请参阅for<> 语法与常规生命周期限制有何不同? .

HRTB(Higher-Ranked Traits Bounds)意味着生命周期可以是任何生命周期。 “任何生命周期”当然包括'static 并且因为'static是可能的最长生命周期,编译器只需使用它,就好像您已经编写了impl Fn(HoldsRef<'_>) 请注意,这仅从回调的角度来看是正确的,也就是说,回调必须能够处理这种情况,但modify()可以在它想要的任何生命周期内调用它,不仅是 static(与如果你会发生的情况相反) d 指定impl Fn(HoldsRef<'static>) ,然后你必须传递一个HoldsRef<'static> )。

因为HoldsRef::set()方法需要&'a i64 ,而'a'static ,所以它就像需要&'static i64 现在您将了解为什么当您提供较短的生命周期时编译器会抱怨。

您可能认为解决方案只是采用通用生命周期而不是 HRTB,并将其绑定到self

fn modify<'a>(&'a mut self, continuation: impl Fn(HoldsRef<'a>)) {

但是它不起作用:

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src/main.rs:19:9
   |
16 |     fn modify<'a>(&'a mut self, continuation: impl Fn(HoldsRef<'a>)) {
   |               -- lifetime `'a` defined here
17 |         let handle = HoldsRef(&mut self.0);
   |                               ----------- first mutable borrow occurs here
18 |         continuation(handle);
   |         -------------------- argument requires that `self.0` is borrowed for `'a`
19 |         self.cleanup();
   |         ^^^^^^^^^^^^^^ second mutable borrow occurs here

这是有道理的:因为我们告诉编译器回调需要一个HoldsRef<'a> ,所以当我们cleanup()时它可以是活动的; 并且因为这个HoldsRef借用self.0 ,所以我们在清理时仍然持有对self.0的可变引用。

不幸的是,我不知道一个好的解决方案。 但也许这里的其他人会:)

您需要指定对内部元素的引用的生命周期应与self相同。 此外,为了实现单个可变引用,您需要拆分为不同的方法并稍后在另一个调用中加入它们:

impl ContainsData {
    fn cleanup(&mut self) {
        self.0 = self.0.abs();
    }
    fn modify<'s>(&'s mut self, continuation: impl FnOnce(HoldsRef<'s>)) {
        let handle = HoldsRef(&mut self.0);
        continuation(handle);
    }
    fn modify_and_cleanup(&mut self, continuation: impl FnOnce(HoldsRef)) {
        self.modify(continuation);
        self.cleanup();
    }
}

操场

暂无
暂无

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

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