You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust结构体构造中Texture2d与Sampler生命周期不匹配问题求助

解决Rust中结构体持有同实例内引用的生命周期问题

我之前也碰到过类似的生命周期困扰,咱们先拆解下你遇到的问题:你想让DefaultResourcePack同时持有Texture2d的所有权和依赖它的Sampler引用,但编译器一直报错,核心原因有两个:

  1. 你定义的Sampler<'a, Texture2d>把生命周期'a绑定到了facade的引用上,但构造函数里创建的texture是局部变量,它的生命周期远短于'a;
  2. 即使你把texture移动到结构体中,Sampler引用的还是构造函数里的那个局部texture实例,当局部变量被移动后,这个引用就失效了——Rust不会自动把引用指向结构体里的新位置。

先看你贴的代码和报错:

你的代码

pub struct DefaultResourcePack<'a> {
    regions: [TexRegion; SHEET_TEX_ID_COUNT],
    sheet: Sampler<'a, Texture2d>,
    simple_program: Program,
    texture: Texture2d,
}
impl<'a> DefaultResourcePack<'a> {
    fn new<F: Facade>(facade: &'a F) -> DefaultResourcePack<'a> {
        // construct regions
        let sheet_dim: f32 = 16.0;
        let slot_size: f32 = 1.0 / sheet_dim;
        let mut regions = [TexRegion::new(0.0, 0.0, 0.0, 0.0); SHEET_TEX_ID_COUNT];
        for &id in &ALL_SHEET_TEX_ID {
            let slot = sheet_tex_ord(id) as f32;
            let ord = sheet_tex_ord(id);
            regions[ord] = TexRegion::uvwh(
                (slot % sheet_dim) * slot_size,
                (slot - (slot % sheet_dim)) * slot_size,
                slot_size,
                slot_size,
            )
        }
        // construct the sheet
        let image = image::load_from_memory(DEF_TEX_SHEET_BYTES)
            .expect("texture sheet load error")
            .to_rgba();
        let image_dim = image.dimensions();
        let image = RawImage2d::from_raw_rgba_reversed(&image.into_raw(), image_dim);
        let texture: Texture2d = Texture2d::new(facade, image).expect("texture creation error");
        let sheet: Sampler<'a, Texture2d> = Sampler::new(&texture).magnify_filter(MagnifySamplerFilter::Nearest);
        // construct the program
        let simple_program = Program::new(
            facade,
            ProgramCreationInput::SourceCode {
                vertex_shader: SIMPLE_SHADER_V,
                tessellation_control_shader: None,
                tessellation_evaluation_shader: None,
                geometry_shader: None,
                fragment_shader: SIMPLE_SHADER_F,
                transform_feedback_varyings: None,
                outputs_srgb: false,
                uses_point_size: false,
            },
        ).expect("GLSL compilation error");
        // construct the pack
        DefaultResourcePack {
            regions,
            sheet,
            simple_program,
            texture,
        }
    }
}

编译器报错

error[E0597]: texture 生命周期不足
--> src\graphics\resourcepack.rs:164:63
|
164 | let sheet: Sampler<'a, Texture2d> = Sampler::new(&texture)
| ^^^^^^^ 被借用的值生命周期不足
...
182 | }
| - 被借用的值仅存活至此处
| note: 被借用的值必须在impl块138:5处定义的生命周期'a内保持有效...
--> src\graphics\resourcepack.rs:138:5
|
138 | / impl<'a> DefaultResourcePack<'a> {
139 | | fn new<F: Facade>(facade: &'a F) -> DefaultResourcePack<'a> {
140 | | // construct regions
141 | | let sheet_dim: f32 = 16.0;
...
182 | | }
183 | | }
| |_____^

解决方案

方案1:用Rc共享所有权(最常用)

这个方法不需要修改太多代码,核心是让Texture2d的所有权被结构体和Sampler共享,这样就不存在引用失效的问题了:

use std::rc::Rc;

// 移除结构体的生命周期参数,因为不需要绑定外部引用了
pub struct DefaultResourcePack {
    regions: [TexRegion; SHEET_TEX_ID_COUNT],
    // Sampler持有Rc<Texture2d>的引用(或者直接持有Rc,看Sampler的定义)
    sheet: Sampler<'static, Rc<Texture2d>>,
    simple_program: Program,
    texture: Rc<Texture2d>,
}

impl DefaultResourcePack {
    // 这里也不需要把facade的生命周期绑定到返回值了
    fn new<F: Facade>(facade: &F) -> DefaultResourcePack {
        // 前面的regions逻辑完全不变
        let sheet_dim: f32 = 16.0;
        let slot_size: f32 = 1.0 / sheet_dim;
        let mut regions = [TexRegion::new(0.0, 0.0, 0.0, 0.0); SHEET_TEX_ID_COUNT];
        for &id in &ALL_SHEET_TEX_ID {
            let slot = sheet_tex_ord(id) as f32;
            let ord = sheet_tex_ord(id);
            regions[ord] = TexRegion::uvwh(
                (slot % sheet_dim) * slot_size,
                (slot - (slot % sheet_dim)) * slot_size,
                slot_size,
                slot_size,
            )
        }

        // 把texture包装成Rc
        let image = image::load_from_memory(DEF_TEX_SHEET_BYTES)
            .expect("texture sheet load error")
            .to_rgba();
        let image_dim = image.dimensions();
        let image = RawImage2d::from_raw_rgba_reversed(&image.into_raw(), image_dim);
        let texture = Rc::new(Texture2d::new(facade, image).expect("texture creation error"));
        // 用Rc的引用来创建Sampler
        let sheet = Sampler::new(&texture).magnify_filter(MagnifySamplerFilter::Nearest);

        // program逻辑不变
        let simple_program = Program::new(
            facade,
            ProgramCreationInput::SourceCode {
                vertex_shader: SIMPLE_SHADER_V,
                tessellation_control_shader: None,
                tessellation_evaluation_shader: None,
                geometry_shader: None,
                fragment_shader: SIMPLE_SHADER_F,
                transform_feedback_varyings: None,
                outputs_srgb: false,
                uses_point_size: false,
            },
        ).expect("GLSL compilation error");

        DefaultResourcePack {
            regions,
            sheet,
            simple_program,
            texture,
        }
    }
}

原理:Rc是引用计数智能指针,结构体和Sampler各自持有一个指向Texture2d的引用,只要其中一个存在,Texture2d就不会被销毁,完美解决生命周期不匹配的问题。

方案2:用自引用结构体(适合严格需要所有权的场景)

如果你不想用Rc,可以用ouroboros crate创建自引用结构体,让Sampler直接引用结构体内部的Texture2d:

首先在Cargo.toml中添加依赖:

ouroboros = "0.15" # 版本号可以根据最新情况调整

然后修改代码:

use ouroboros::self_referencing;

#[self_referencing]
pub struct DefaultResourcePack {
    regions: [TexRegion; SHEET_TEX_ID_COUNT],
    simple_program: Program,
    // 持有Texture2d的所有权
    texture: Texture2d,
    // 标注这个字段借用texture,使用结构体内部的生命周期'this
    #[borrows(texture)]
    #[covariant]
    sheet: Sampler<'this, Texture2d>,
}

impl DefaultResourcePack {
    fn new<F: Facade>(facade: &F) -> Self {
        // regions逻辑不变
        let sheet_dim: f32 = 16.0;
        let slot_size: f32 = 1.0 / sheet_dim;
        let mut regions = [TexRegion::new(0.0, 0.0, 0.0, 0.0); SHEET_TEX_ID_COUNT];
        for &id in &ALL_SHEET_TEX_ID {
            let slot = sheet_tex_ord(id) as f32;
            let ord = sheet_tex_ord(id);
            regions[ord] = TexRegion::uvwh(
                (slot % sheet_dim) * slot_size,
                (slot - (slot % sheet_dim)) * slot_size,
                slot_size,
                slot_size,
            )
        }

        // 创建texture和program
        let image = image::load_from_memory(DEF_TEX_SHEET_BYTES)
            .expect("texture sheet load error")
            .to_rgba();
        let image_dim = image.dimensions();
        let image = RawImage2d::from_raw_rgba_reversed(&image.into_raw(), image_dim);
        let texture = Texture2d::new(facade, image).expect("texture creation error");

        let simple_program = Program::new(
            facade,
            ProgramCreationInput::SourceCode {
                vertex_shader: SIMPLE_SHADER_V,
                tessellation_control_shader: None,
                tessellation_evaluation_shader: None,
                geometry_shader: None,
                fragment_shader: SIMPLE_SHADER_F,
                transform_feedback_varyings: None,
                outputs_srgb: false,
                uses_point_size: false,
            },
        ).expect("GLSL compilation error");

        // 用builder模式创建自引用结构体
        DefaultResourcePackBuilder {
            regions,
            simple_program,
            texture,
            // 这里传入闭包,用来创建依赖texture的sheet
            sheet_builder: |texture| Sampler::new(texture).magnify_filter(MagnifySamplerFilter::Nearest),
        }.build()
    }
}

原理:ouroboros的self_referencing宏会帮你处理自引用的生命周期安全,确保sheet永远只能引用结构体内部的texture,不会出现悬空引用。

内容的提问来源于stack exchange,提问作者Phoenix

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:53:02