How to avoid GATs

Hi there!

GATs aren't stable:


But I have an API I'm building that would naively require them:

pub trait Shader {
    type Vertex<'a>;
    fn push<'a>(&mut self, vertices: Self::Vertex<'a>);
}
impl Shader for Foo {
    type Vertex<'a> = (
        &'a [[f32; 2]],  //Pos
        &'a [[f32; 4]],  //Colour
    );
    fn push<'a>(&mut self, (positions, colours): Self::Vertex<'a>) {
        ///
    }
}

I'm writing an opengl wrapper for a project, the aim of which is to serve as a cross between Piston and 3D, with the ability to do custom stuff, and run on opengles 3.2. Rather specific, and I feel that glium isn't something I want to learn on top of OpenGL given I'm relatively inexperienced with it.

Back to the problem at hand; I want to keep the attributes apart and not interleaved to not complicate my shader-related code, so the following is not really what I want:

trait Shader {
    type Vertex;
    fn push(&mut self, vertices: &[Self::Vertex]);
}
impl Shader for Foo {
    type Vertex = ([f32; 2], [f32; 4]);
    fn push(&mut self, vertices: &[Self::Vertex]) { /**/ }
}

How might I design this to avoid the use of GATs. Raw pointers (type Vertex = (*const [f32; 2], *const [f32; 4]);) would be a last resort.
Thanks!

1 Like

Could you pull the lifetime up into the trait itself, so declare trait Shader<'a>?

Another option is to make your shader generic over the vertex buffer (e.g. Shader<T>) and then do something like impl<'a> Shader<(&'a [[f32; 2]], &'a [[f32; 4]])> for Foo.

4 Likes

Suggest writing (alternative) calling code to decide which way gives least friction. First of @Michael-F-Bryan sounds simplest, only examples of use uncover (or not) what you find best.

This is a higher kind example.

3 Likes

If I must be honest, I did not understand @Michael-F-Bryan's first solution; I did not think of HRTBs, although, @jonh exemplified how to use @Michael-F-Bryan's first solution, so I believe the both of you should recieve the solution.

You don't see a trait with an attached lifetime very often, but here's an example of it being used in serde::Deserialize.

pub trait Deserialize<'de>: Sized {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>;
}
2 Likes