假设我有一个带有引用的结构,另一个带有该结构引用的结构,如下所示:
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
类(也有一个引用)时,我没有做任何这样的事情,并且调整了抵消内联。
首先,我甚至不明白为什么这是必要的(诚然,我对铁锈寿命的理解是有限的)。 其次(我的主要问题),我能做些什么 SubImage
以使这些明确的生命周期不必要吗?
答案 0 :(得分:7)
是的,这个错误可能会让人感到困惑,但这是有正当理由的。
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>)
因此,它尝试使用SubImage
创建&'b mut Image<'a>
,其中'b
不一定等于'a
(在这种特殊情况下,它肯定不会),所以编译器纾困了。
此类签名也是您在&mut image
中提供main()
时调用此功能的唯一原因,因为&mut image
的生命周期为image
变量,但Image
包含在此变量中的1}}的生命周期为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>)
你确实做了&#34;推&#34;上游问题 - 现在根据&mut image
无法调用该函数,您现在可以看到原因 - 它需要统一'a
和'b
生命周期,这是不可能的因为'a
长于'b
。
正确的解决方案是在Image
定义中使用单独的生命周期来引用Image
和SubImage
本身:
struct SubImage<'b, 'a:'b> {
image: &'b mut Image<'a>,
offset: (i32, i32),
size: (i32, i32),
}
现在'b
和'a
的生命周期可能不同,但为了实现这一点,您必须将'a
生命周期与'b
绑定,即{{1}必须至少与'a
一样长。这正是您的代码所需的语义。如果没有强制执行此约束,那么引用的图像可能会导致&#34; die&#34;在引用它之前超出范围,这违反了Rust的安全规则。
答案 1 :(得分:3)
我可以对SubImage做些什么来使这些显性生命周期不必要吗?
弗拉基米尔的回答很明显,但我鼓励你稍微改变你的代码。我的许多原始代码对引用的东西都有非常相似的引用。 如果你需要它,那么拥有不同的生命周期可以帮助很多。但是,我只是将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),
}
就我而言,通过嵌套引用我并没有真正获得任何东西。直接嵌入结构会使它更大一些,但可以使访问速度更快(少一个指针追逐)。重要的是,在这种情况下,它消除了第二次生命的需要。