In Rust, how do you explicitly tie the lifetimes o

2020-04-08 14:58发布

问题:

The specific case where I ran into this was in using OpenGL, writing structs for a VertexBuffer and VertexArray. Each struct is, in essence, a single GLuint that refers to the OpenGL object. In the simplest case, a VertexArray has exactly one VertexBuffer associated with it.

The problem is that the VertexArray cannot live longer than its associated VertexBuffer. Rust doesn't know this though, since the reference that the VertexArray holds is internal to OpenGL, so it will have no problem calling the destructor on the VertexBuffer while an existing VertexArray references it.

The current solution I have is to put a reference in manually, which goes unused:

struct VertexArray<'a> {
    id: GLuint,
    #[warn(dead_code)]
    vbo: &'a VertexBuffer
}

In more complex cases, the reference might turn out to be necessary, but it feels inelegant and wasteful. A VAO with multiple VBOs could be implemented with an array/vector of references. Being able to change the associated buffers after the VAO has been created might also add this requirement.

Is there any way to achieve this same behaviour without the reference? Or, since the compiler can recognize that the reference is never used and give a warning, will it be optimized out?

回答1:

This is one of the primary use cases for the PhantomData type, as demonstrated there in an example.

Applied to this case, you’ll end up with something like this:

use std::marker::PhantomData;

struct VertexArray<'a> {
    id: GLuint,
    vbo_lifetime: PhantomData<&'a VertexBuffer>,
}

And instantiation will be something like this:

    fn make<'a>(&'a self) -> VertexArray<'a> {
        VertexArray {
            id: …,
            vbo_lifetime: PhantomData,
        }
    }

(This is eliding the generic type, allowing it to be inferred; you could also write PhantomData::<&'a VertexBuffer>.)