生命周期和对包含引用的对象的引用

Ben*_*ley 7 reference lifetime rust

假设我有一个带有引用的结构,另一个带有该结构引用的结构,如下所示:

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),
}
Run Code Online (Sandbox Code Playgroud)

结构具有几乎相同的接口,不同之处在于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);
}
Run Code Online (Sandbox Code Playgroud)

然后我创建了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);
}
Run Code Online (Sandbox Code Playgroud)

但是,这会导致编译器错误:

main.rs:62:16: 62:21 error: cannot infer an appropriate lifetime for automatic coercion due to conflicting requirements
Run Code Online (Sandbox Code Playgroud)

编译器的建议是将签名更改为:

fn render2<'a>(image: &'a mut Image<'a>)
Run Code Online (Sandbox Code Playgroud)

然而,这只是把问题推到了调用的函数render2,然后拿了一个&mut Image.这是非常烦人的,因为函数调用深入了几层,当我刚刚使用Image类(也有一个引用)并调整内联偏移时,我不必执行任何操作.

首先,我甚至不明白为什么这是必要的(诚然,我对铁锈寿命的理解是有限的).其次(我的主要问题),我能做些什么 SubImage 才能使这些明确的生命周期不必要?

Vla*_*eev 7

是的,这个错误可能令人困惑,但是有合理的理由.

struct SubImage<'a> {
    image: &'a mut Image<'a>,
    offset: (i32, i32),
    size: (i32, i32),
}
Run Code Online (Sandbox Code Playgroud)

在这里,您声明Image必须使用的引用与图像本身内部借用的数据完全相同 - 'a在引用中使用相同的生命周期参数,并将其作为参数用于Image:&'a mut Image<'a>.

但是,render2()违反了这一要求.实际签名render2()如下:

fn render2<'b, 'a>(image: &'b mut Image<'a>)
Run Code Online (Sandbox Code Playgroud)

因此,它试图创建SubImagewith &'b mut Image<'a>,其中'b不一定等于'a(在这种特殊情况下,它肯定不会),因此编译器挽救.

同样,这种签名是你可以调用这个函数,同时提供它的唯一原因&mut imagemain(),因为&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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

当你声明render2()

fn render2<'a>(image: &'a mut Image<'a>)
Run Code Online (Sandbox Code Playgroud)

你确实在上游"推"问题 - 现在根本无法调用该函数&mut image,你现在可以看到原因了 - 它需要统一'a'b生命周期,这是不可能的,因为'a它比它长'b.

适当的解决方案是使用单独的寿命为参考ImageImage本身SubImage的定义:

struct SubImage<'b, 'a:'b> {
    image: &'b mut Image<'a>,
    offset: (i32, i32),
    size: (i32, i32),
}
Run Code Online (Sandbox Code Playgroud)

现在'b'a可能是不同的生命周期,虽然为了使这个工作你必须限制'a一生'b,也就是说,'a必须至少生活'b.这正是您的代码所需的语义.如果未强制执行此约束,则引用的图像可能会在引用它之前"死亡"超出范围,这违反了Rust的安全规则.