1

I have a constructor for my struct that creates a Texture2d and a Sampler<'a, Texture2d> where 'a is the lifetime of the Texture2d. I need to store the Sampler in the struct I am constructing.

If I try to return a struct containing the Sampler<'a, Texture2d> and then the Texture2d goes out of scope, then the struct, which is called DefaultResourcePack<'a> would contain a reference of lifetime 'a after that lifetime is over, which wouldn't be valid. The compiler agrees with me - it's not compiling.

I attempt to solve this by giving the DefaultResourcePack ownership of the Texture2d and hoping that the compiler will recognize that the lifetimes are now valid, but the compiler is still complaining.

How can I solve this?

Here's the relevant code:

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,
        }
    }
}

Here's what the compiler says:

error[E0597]: `texture` does not live long enough
   --> src\graphics\resourcepack.rs:164:63
    |
164 |             let sheet: Sampler<'a, Texture2d> = Sampler::new(&texture)
    |                                                               ^^^^^^^ borrowed value does not live long enough
...
182 |         }
    |         - borrowed value only lives until here
    |
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 138:5...
   --> 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 | |     }
    | |_____^
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Phoenix
  • 1,553
  • 2
  • 13
  • 29
  • 1
    If the struct can be moved, it will invalidate the reference. Therefore, the only way to make this work is to make the struct immovable, which means you can't return it from a function (because returning is a move). – trent Jan 26 '18 at 23:31

0 Answers0