[英]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.