How do you determine the lifespan in related types?
I am trying to get each implementation GraphicsContext
to return a different implementation Shader
.
pub trait Resources { type Shader: shader::Shader; } pub trait GraphicsContext { type Resources: Resources; /// Creates a shader object fn create_shader<'a>(&'a self, shader::Stage, source: &str) -> Result<<<Self as GraphicsContext>::Resources as Resources>::Shader, shader::CreateError>; /// Creates a shader program object fn create_shader_program<'a>(&'a self, shaders: Vec<&shader::Shader>) -> Result<Box<shader::ShaderProgram + 'a>, shader::ProgramCreateError>; // more to come }
This means that the method create_shader_program
(and other methods) knows a specific type Shader
so that they can call specific implementation methods on the shader object.
I don't want to put these methods (like setCurrent
or attach
) in a trait that all implementations should use. Not all graphics APIs use the same system: OpenGL is bind / unbind, Vulkan will be structs / given fields, DirectX is something else, etc.
First, I ask what is the correct way to structure my engine. I believe that in my framework / application code that requires these objects Shader
, I can specify a specific type based on the current type Context
.
// almost certain this doesn't compile, but should be possible in theory // I'm trying to say: // the type of the `shader` argument must match to the associated type // contained within the `context` fn do_something_with_shader(context: &GraphicsContext, shader: ??**GraphicsContext::Resources::Shader**??) -> Result<Foo, Bar>;
or perhaps:
fn do_something_with_shader<T>(context: &GraphicsContext<T>, shader: ??**T::Shader**??) where T: GraphicsContext::Resources -> Result<Foo, Bar>;
Is something like this possible? Hope you can see how I understand basic generics (I come from Java), but this is driving me crazy (this is a very brave person).
If this is the right way, then there is a problem with my implementation. rustc
wants the associated type to have the specified lifetime.
wrong number of lifetime parameters: expected 1, found 0 [E0107] opal_driver_gl/src/context.rs:23 type Shader = Shader;
My OpenGLShader
struct is actually of type OpenGLShader<'a>
, so the error makes sense. My question is where can I get my whole life out of this bunch of code:
struct OpenGLResources; impl Resources for OpenGLResources { type Shader = OpenGLShader; } impl GraphicsContext for OpenGLGraphicsContext { type Resources = Resources; /// Creates a shader object fn create_shader<'a>(&'a self, stage: core_shader::Stage, source: &str) -> Result<<<Self as GraphicsContext>::Resources as Resources>::Shader, core_shader::CreateError> { // impl goes here } }
I tried to bind the lifetime to OpenGLResources
and OpenGLGraphicsContext
, which solved the error, but then said error: parameter 'a is never used
.
So, secondly, I am asking how to include this lifetime in my bound type .
Thanks a lot if you can take a look at this. I feel like something like this should be checked at compile time, but I'm pretty new to Rust so I just can't figure out how to implement it.
source to share
I ended up switching to generics, which ensured a successful implementation.
Given Resources<'a>
, I can define GraphicsContext
like this:
trait GraphicsContext<'a, R: Resources<'a>>
For Shader<'a>
and ShaderProgram<'a>
structures insideResources
it takes 2
'a
lifetimes above.
Then an OpenGL implementation can be implemented. Please note what has Resources
been changed to OpenGLResources
.
// replaced here
impl<'a> GraphicsContext<'a, OpenGLResources<'a>> for OpenGLGraphicsContext<'a> {
fn create_shader(&'a self, ty: Type, source: &str) -> Result<Shader<'a>, ShaderCreationError> {
// do something
}
}
source to share