生命周期和对包含引用的对象的引用
Lifetimes and references to objects containing references
假设我有一个包含引用的结构,以及另一个包含对该结构的引用的结构,如下所示:
struct Image<'a> {
pixel_data: &'a mut Vec<u8>,
size: (i32, i32),
}
struct SubImage<'a> {
image: &'a mut Image<'a>,
offset: (i32, i32),
size: (i32, i32),
}
这些结构具有几乎相同的接口,不同之处在于 SubImage
在转发到包含的 Image
引用的相应函数之前根据其偏移量调整位置参数。我希望这些结构大部分是可以互换的,但我似乎无法弄清楚如何使生命周期正确。最初,我只是使用 Image
,并且可以简单地传递对象,而无需使用生命周期说明符:
fn main() {
let mut pixel_data: Vec<u8> = Vec::new();
let mut image = Image::new(&mut pixel_data, (1280, 720));
render(&mut image);
}
fn render(image: &mut Image) {
image.rect_fill(0, 0, 10, 10);
}
然后我创建了 SubImage
,并想做这样的事情:
fn render2(image: &mut Image) {
let mut sub = SubImage {
image: image, // line 62
offset: (100, 100),
size: (600, 400),
};
sub.rect_fill(0, 0, 10, 10);
}
但是,这会导致编译器错误:
main.rs:62:16: 62:21 error: cannot infer an appropriate lifetime for automatic coercion due to conflicting requirements
编译器的建议是将签名更改为:
fn render2<'a>(image: &'a mut Image<'a>)
然而,这只会将问题推到调用 render2
并占用 &mut Image
的函数上。这很烦人,因为函数调用深入了几层,当我只使用 Image
class (也有参考)时我不需要做任何这些,并调整内联偏移量。
所以首先,我什至不明白为什么这是必要的(诚然,我对锈蚀寿命的理解是有限的)。 其次(我的主要问题),我能做些什么来 SubImage
使这些明确的生命周期变得不必要?
是的,这个错误可能令人困惑,但有一个合理的理由。
struct SubImage<'a> {
image: &'a mut Image<'a>,
offset: (i32, i32),
size: (i32, i32),
}
在这里声明对 Image
的引用必须 与图像本身借用的数据一样长 - 相同的生命周期参数 'a
既用于参考又用作 Image
的参数:&'a mut Image<'a>
.
然而,render2()
违反了这个要求。 render2()
实际签名如下:
fn render2<'b, 'a>(image: &'b mut Image<'a>)
因此,它尝试用 &'b mut Image<'a>
创建 SubImage
,其中 'b
不一定等于 'a
(在这种特殊情况下,它肯定不会),因此编译器会退出。
此外,这种签名是您在 main()
中提供 &mut image
时可以调用此函数的唯一原因,因为 &mut image
具有 image
变量的生命周期,但是此变量中包含的 Image
的生命周期为 pixel_data
,稍长一些。以下代码不是有效的 Rust,但它接近于编译器理解事物的方式并且它演示了问题:
fn main() {
'a: {
let mut pixel_data: Vec<u8> = Vec::new();
'b: {
let mut image: Image<'a> = Image::new(&'a mut pixel_data, (1280, 720));
render2::<'b, 'a>(&'b mut image);
}
}
}
当您将 render2()
声明为
时
fn render2<'a>(image: &'a mut Image<'a>)
你确实在上游 "push" 遇到了问题 - 现在根本无法使用 &mut image
调用该函数,你现在可以明白为什么 - 它需要统一 'a
和 'b
生命周期,这是不可能的,因为 'a
比 'b
.
长
正确的解决方案是在 SubImage
定义中使用单独的生命周期来引用 Image
和 Image
本身:
struct SubImage<'b, 'a:'b> {
image: &'b mut Image<'a>,
offset: (i32, i32),
size: (i32, i32),
}
现在 'b
和 'a
可能是不同的生命周期,尽管为了使其工作,您必须将 'a
生命周期与 'b
绑定,也就是说,'a
必须至少活到 'b
。这正是您的代码需要的语义。如果不强制执行此约束,则引用的图像可能会在引用超出范围之前 "die",这违反了 Rust 的安全规则。
is there anything I can do to SubImage to make these explicit lifetimes not necessary?
Vladimir 的回答很准确,但我鼓励您稍微更改一下代码。我的很多原始代码对带引用的事物都有非常相似的引用。 如果你需要它,那么拥有独立的生命周期会有很大帮助。但是,我只是将 Image
嵌入 SubImage
:
struct Image<'a> {
pixel_data: &'a mut Vec<u8>,
size: (i32, i32),
}
struct SubImage<'a> {
image: Image<'a>,
offset: (i32, i32),
size: (i32, i32),
}
就我而言,嵌套引用并没有给我带来任何好处。直接嵌入结构会使它更大一点,但是 can 可以使访问更快一点(少了一个指针追逐)。重要的是,在这种情况下,它消除了第二次生命的需要。
假设我有一个包含引用的结构,以及另一个包含对该结构的引用的结构,如下所示:
struct Image<'a> {
pixel_data: &'a mut Vec<u8>,
size: (i32, i32),
}
struct SubImage<'a> {
image: &'a mut Image<'a>,
offset: (i32, i32),
size: (i32, i32),
}
这些结构具有几乎相同的接口,不同之处在于 SubImage
在转发到包含的 Image
引用的相应函数之前根据其偏移量调整位置参数。我希望这些结构大部分是可以互换的,但我似乎无法弄清楚如何使生命周期正确。最初,我只是使用 Image
,并且可以简单地传递对象,而无需使用生命周期说明符:
fn main() {
let mut pixel_data: Vec<u8> = Vec::new();
let mut image = Image::new(&mut pixel_data, (1280, 720));
render(&mut image);
}
fn render(image: &mut Image) {
image.rect_fill(0, 0, 10, 10);
}
然后我创建了 SubImage
,并想做这样的事情:
fn render2(image: &mut Image) {
let mut sub = SubImage {
image: image, // line 62
offset: (100, 100),
size: (600, 400),
};
sub.rect_fill(0, 0, 10, 10);
}
但是,这会导致编译器错误:
main.rs:62:16: 62:21 error: cannot infer an appropriate lifetime for automatic coercion due to conflicting requirements
编译器的建议是将签名更改为:
fn render2<'a>(image: &'a mut Image<'a>)
然而,这只会将问题推到调用 render2
并占用 &mut Image
的函数上。这很烦人,因为函数调用深入了几层,当我只使用 Image
class (也有参考)时我不需要做任何这些,并调整内联偏移量。
所以首先,我什至不明白为什么这是必要的(诚然,我对锈蚀寿命的理解是有限的)。 其次(我的主要问题),我能做些什么来 SubImage
使这些明确的生命周期变得不必要?
是的,这个错误可能令人困惑,但有一个合理的理由。
struct SubImage<'a> {
image: &'a mut Image<'a>,
offset: (i32, i32),
size: (i32, i32),
}
在这里声明对 Image
的引用必须 与图像本身借用的数据一样长 - 相同的生命周期参数 'a
既用于参考又用作 Image
的参数:&'a mut Image<'a>
.
然而,render2()
违反了这个要求。 render2()
实际签名如下:
fn render2<'b, 'a>(image: &'b mut Image<'a>)
因此,它尝试用 &'b mut Image<'a>
创建 SubImage
,其中 'b
不一定等于 'a
(在这种特殊情况下,它肯定不会),因此编译器会退出。
此外,这种签名是您在 main()
中提供 &mut image
时可以调用此函数的唯一原因,因为 &mut image
具有 image
变量的生命周期,但是此变量中包含的 Image
的生命周期为 pixel_data
,稍长一些。以下代码不是有效的 Rust,但它接近于编译器理解事物的方式并且它演示了问题:
fn main() {
'a: {
let mut pixel_data: Vec<u8> = Vec::new();
'b: {
let mut image: Image<'a> = Image::new(&'a mut pixel_data, (1280, 720));
render2::<'b, 'a>(&'b mut image);
}
}
}
当您将 render2()
声明为
fn render2<'a>(image: &'a mut Image<'a>)
你确实在上游 "push" 遇到了问题 - 现在根本无法使用 &mut image
调用该函数,你现在可以明白为什么 - 它需要统一 'a
和 'b
生命周期,这是不可能的,因为 'a
比 'b
.
正确的解决方案是在 SubImage
定义中使用单独的生命周期来引用 Image
和 Image
本身:
struct SubImage<'b, 'a:'b> {
image: &'b mut Image<'a>,
offset: (i32, i32),
size: (i32, i32),
}
现在 'b
和 'a
可能是不同的生命周期,尽管为了使其工作,您必须将 'a
生命周期与 'b
绑定,也就是说,'a
必须至少活到 'b
。这正是您的代码需要的语义。如果不强制执行此约束,则引用的图像可能会在引用超出范围之前 "die",这违反了 Rust 的安全规则。
is there anything I can do to SubImage to make these explicit lifetimes not necessary?
Vladimir 的回答很准确,但我鼓励您稍微更改一下代码。我的很多原始代码对带引用的事物都有非常相似的引用。 如果你需要它,那么拥有独立的生命周期会有很大帮助。但是,我只是将 Image
嵌入 SubImage
:
struct Image<'a> {
pixel_data: &'a mut Vec<u8>,
size: (i32, i32),
}
struct SubImage<'a> {
image: Image<'a>,
offset: (i32, i32),
size: (i32, i32),
}
就我而言,嵌套引用并没有给我带来任何好处。直接嵌入结构会使它更大一点,但是 can 可以使访问更快一点(少了一个指针追逐)。重要的是,在这种情况下,它消除了第二次生命的需要。