繁体   English   中英

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

[英]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类(也有一个引用)并调整内联偏移时,我不必执行任何操作。

首先,我甚至不明白为什么这是必要的(诚然,我对铁锈寿命的理解是有限的)。 其次(我的主要问题),有什么我可以做的 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>)

你确实向上游“推”了问题 - 现在用&mut image完全无法调用这个函数,现在你可以看到为什么 - 它需要统一'a'b生命周期,这是不可能的,因为'a更长比'b

正确的解决方案是在SubImage定义中使用单独的生命周期来引用ImageImage本身:

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

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

有什么我可以做的SubImage使这些显性生命周期没有必要吗?

弗拉基米尔的回答很明显,但我鼓励你稍微改变你的代码。 我的许多原始代码对引用的东西都有非常相似的引用。 如果您需要,那么拥有不同的生命周期可以提供很多帮助。 但是,我只是在SubImage嵌入了Image

struct Image<'a> {
    pixel_data: &'a mut Vec<u8>,
    size: (i32, i32),
}

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

就我而言,通过嵌套引用,我并没有真正获得任何东西。 直接嵌入结构使它更大一些,但可以使访问速度更快(少一个指针追逐)。 重要的是,在这种情况下,它消除了第二次生命的需要。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM