Lifetimes and references to objects containing references

早过忘川 提交于 2019-12-05 06:29:28

Yes, this error may be confusing but there is a legitimate reason for it.

struct SubImage<'a> {
    image: &'a mut Image<'a>,
    offset: (i32, i32),
    size: (i32, i32),
}

Here you declare that the reference to Image must live exactly as long as the borrowed data inside the image itself - the same lifetime parameter 'a is used both in the reference and as a parameter for Image: &'a mut Image<'a>.

However, render2() violates this requirement. The actual signature of render2() is as follows:

fn render2<'b, 'a>(image: &'b mut Image<'a>)

Therefore, it tries to create SubImage with &'b mut Image<'a>, where 'b not necessarily equals to 'a (and in this particular case, it most certainly does not), and so the compiler bails out.

Also such signature is the only reason you can call this function while providing it &mut image in main(), because &mut image have lifetime of image variable, but the Image contained inside this variable has lifetime of pixel_data which is slightly longer. The following code is not valid Rust, but it is close to how the compiler understands things and it demonstrates the problem:

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);
        }
    }
}

When you declare render2() as

fn render2<'a>(image: &'a mut Image<'a>)

you indeed do "push" the problem upstream - now the function can't be called at all with &mut image, and you can now see why - it would require unifying 'a and 'b lifetimes, which is impossible because 'a is longer than 'b.

The proper solution is to use separate lifetimes for reference to Image and Image itself in SubImage definition:

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

Now 'b and 'a may be different lifetimes, though in order for this to work you have to bound 'a lifetime with 'b, that is, 'a must live at least as long as 'b. This is exactly the semantics your code needs. If this constraint is not enforced, then it would be possible for the referenced image to "die" before the reference to it goes out of scope, which is a violation of safety rules of Rust.

is there anything I can do to SubImage to make these explicit lifetimes not necessary?

Vladimir's answer is spot on, but I'd encourage you to change your code a bit. A lot of my original code had very similar references to things with references. If you need that, then having separate lifetimes can help a lot. However, I'd just embed the Image in 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),
}

In my case, I wasn't really gaining anything by having nested references. Embedding the struct directly makes it a bit bigger, but can make access a bit faster (one less pointer chase). Importantly in this case, it removes the need for a second lifetime.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!