Why is this trait/implementation incompatible - bound lifetime vs concrete lifetime

149 views Asked by At

I'm struggling with this error rustc gives me:

error: method `create_shader_explicit` has an incompatible type for trait: expected bound lifetime parameter 'a, found concrete lifetime

My trait declaration is pretty much this:

pub trait GraphicsContext<R: Resources> {

    /// Creates a shader object
    fn create_shader<'a>(&'a self, shader::Stage, source: &str) -> 
        Result<handle::Shader<R>, shader::CreateError>;

}

Here's my implementation,

pub struct OpenGLResources<'a> {
    phantom: PhantomData<&'a u32> 
}

impl<'a> Resources for OpenGLResources<'a> {
    type Shader = Shader<'a>;
}

impl<'z> GraphicsContext<OpenGLResources<'z>> for OpenGLGraphicsContext {

    /// Creates a shader object
    fn create_shader<'a>(&'a self, stage: shader::Stage, source: &str) -> 
        Result<handle::Shader<OpenGLResources>, shader::CreateError> {

        let shader = Shader::new(self, stage);
        try!(shader.compile_from_source(source));

        Ok(shader)
    }

}

In other questions on StackOverflow, they are missing things like <'a> between create_shader and (), however when I compare the fn definitions in mine they look identical.

EDIT:

Changing the definition inside impl to the following fixes that issue

fn create_shader<'a>(&'a self, stage: shader::Stage, source: &str) ->     
    Result<handle::Shader<OpenGLResources**<'z>**>, shader::CreateError>

But then the issue is that 'a and 'z need to be the same lifetime. If I change it to this:

fn create_shader(**&'z** self, stage: shader::Stage, source: &str) -> 
    Result<handle::Shader<OpenGLResources<'z>>, shader::CreateError>

The impl block works, but then I need a way of specifying the 'z lifetime in the trait definition. I tried the following:

pub trait<'z> GraphicsContext<R: Resources<'z>>

But it didn't work.

2

There are 2 answers

0
neon64 On BEST ANSWER

Thanks the hints of @Chris Morgan I managed to implement this functionality and its now working fine.

If we start with the base trait with the 'a lifetime included:

trait Resources<'a> {
    type Shader: Shader;
    type ShaderProgram: ShaderProgram;
}

Then implement it for OpenGL. (note the PhantomData struct)

struct OpenGLResources<'a> {
   phantom: PhantomData<&'a u32> // 'a is the lifetime of the context reference
}

impl<'a> ResourcesTrait<'a> for Resources<'a> {
    type Shader = Shader<'a>;
    type ShaderProgram = ShaderProgram<'a>;
    type CommandBuffer = CommandBuffer;
    type CommandBufferBuilder = CommandBufferBuilder;
} 

Its a bit verbose, but the GraphicsContext trait works fine too now. The 'a lifetime goes in the type parameters part.

trait GraphicsContext<'a, R: Resources<'a>> {

    fn create_shader(&'a self, ty: Type, source: &str) -> Result<R::Shader, ShaderCreationError>

}

Finally this is the required code in the graphics context implementation. It is extremely verbose with the 'a lifetimes sprinkled everywhere but at least it works!

impl<'a> GraphicsContext<'a, Resources<'a>> for OpenGLGraphicsContext
2
Chris Morgan On

When comparing things like this, you need to remember to expand all the generics so that you can actually compare it all. In this case, you haven’t expanded R. If you do, the answer becomes obvious: R is OpenGLResources<'z>, linking the OpenGLResources to the impl block, whereas your method definition has elided the lifetime on OpenGLResources, causing it to be inferred as self’s lifetime, which is 'a.