与关联类型相关的生命周期问题

5

最近一周,我因为生命周期的问题而苦恼不已。

当我尝试在DataSource中引用Buffer的引用,并将其引用到DrawCommand中时,就会出现问题。我收到了这个错误:vertex_data_source 没有足够长的生命周期

src/main.rs:65:23: 65:41 error: 
src/main.rs:65         data_source: &vertex_data_source
                                     ^~~~~~~~~~~~~~~~~~
src/main.rs:60:51: 67:2 note: reference must be valid for the block suffix following statement 3 at 60:50...
src/main.rs:60     let vertices = VertexAttributes::new(&buffer);
src/main.rs:61
src/main.rs:62     let vertex_data_source = factory.create_data_source(vertices);
src/main.rs:63
src/main.rs:64     let command: DrawCommand<ResourcesImpl> = DrawCommand {
src/main.rs:65         data_source: &vertex_data_source
               ...
src/main.rs:62:67: 67:2 note: ...but borrowed value is only valid for the block suffix following statement 4 at 62:66
src/main.rs:62     let vertex_data_source = factory.create_data_source(vertices);
src/main.rs:63
src/main.rs:64     let command: DrawCommand<ResourcesImpl> = DrawCommand {
src/main.rs:65         data_source: &vertex_data_source
src/main.rs:66     };
src/main.rs:67 }

它说vertex_data_source必须在第60行后的块后缀语句中有效。我对这个错误的解释是,vertex_data_source应该在第60行之前被定义。但是,要首先创建vertex_data_source,我需要访问第60行上的那些VertexAttributes,所以我不能只是交换顺序。
我觉得我的代码中所有分散的'a生命周期需要拆分成2个或者只是删除,然而我尝试了每一个看起来合理的组合,但是我没有新的想法。
下面是一个大大简化了的示例代码,它演示了问题。我真的很感激您的帮助和建议,希望您能发现问题。(每次在几天的折腾之前都会产生一个修复,但这一次我卡住了)。
use std::cell::RefCell;
use std::marker::PhantomData;

pub struct DrawCommand<'a, R: Resources<'a>> {
    pub data_source: &'a R::DataSource
}

pub trait Resources<'a> {
    type DataSource: 'a;
    type Buffer: 'a;
}

pub struct DataSource<'a> {
    id: u32,
    attributes: Vec<VertexAttributes<'a, ResourcesImpl<'a>>>,
    current_element_array_buffer_binding: RefCell<Option<Buffer<'a>>>
}

pub struct Buffer<'a> {
    context: &'a GraphicsContextImpl
}

pub struct GraphicsContextImpl;

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

impl<'a> Resources<'a> for ResourcesImpl<'a> {
    type Buffer = Buffer<'a>;
    type DataSource = DataSource<'a>;
}

struct Factory<'a> {
    context: &'a GraphicsContextImpl
}

impl<'a> Factory<'a> {
    /// Creates a buffer
    fn create_buffer<T>(&self) -> Buffer<'a> {
        Buffer {
            context: self.context
        }
    }

    fn create_data_source(&self, attributes: Vec<VertexAttributes<'a, ResourcesImpl<'a>>>) -> DataSource<'a> {
        DataSource {
            id: 0,
            attributes: attributes,
            current_element_array_buffer_binding: RefCell::new(None)
        }
    }
}

fn main() {
    let context = GraphicsContextImpl;
    let factory = Factory {
        context: &context
    };
    let buffer = factory.create_buffer::<u32>();

    let vertices = VertexAttributes::new(&buffer);

    let vertex_data_source = factory.create_data_source(vec!(vertices));

    let command: DrawCommand<ResourcesImpl> = DrawCommand {
        data_source: &vertex_data_source
    };
}

pub struct VertexAttributes<'a, R: Resources<'a>> {
    pub buffer: &'a R::Buffer,
}

impl<'a, R: Resources<'a>> VertexAttributes<'a, R> {
    pub fn new(buffer: &'a R::Buffer) -> VertexAttributes<'a, R> {
        VertexAttributes {
            buffer: buffer
        }
    }
}

非常感谢你提前的帮助。

编辑:

我已经更新了代码,以更好地反映我的实际实现。

顺便说一句 - 替换这个:

let vertex_data_source = factory.create_data_source(vec!(vertices));

有了这个:

let vertex_data_source = DataSource {
    id: 0,
    attributes: vec!(vertices),
    current_element_array_buffer_binding: RefCell::new(None)
};

无法解决该问题。


这可能有点冒险,但是你将“vertices”移入“create_data_source”,并且在相同的生命周期内返回,那么当函数返回时,该生命周期是否已经结束? - MartinHaTh
我试图理解这段代码,但你简化了它到一个让我无法理解的程度。虽然解决方案是“删掉一堆看似无用的代码”,但那不太可能是你想要的。例如,为什么attributes被传递给create_data_source却从未被使用?为什么create_data_sourceFactory的一个方法,却从未引用过它?为什么在该方法中使用'a,却与该生命周期没有任何关系?我可以告诉你为什么会出现错误,但如果不清楚你在做什么,我无法开始建议如何修复它... - DK.
这是我放弃之前的进度:http://is.gd/khBtaO - DK.
谢谢@MartinHaTh,它确实起作用了,但只适用于简化的示例。我现在已经更新了我的测试代码,将“attributes”实际存储在“DataSource”中。 - neon64
非常感谢@DK提供的详细分析。我已经更新了代码,以更好地反映我的意图(现在实际使用了attributes)。create_data_source是一个工厂中的方法,因为在我的实际实现中将有许多不同的工厂(OpenGL工厂、DirectX工厂等)。然而,即使我只是在工厂之外创建vertex_data_source,问题仍然存在,所以我认为真正的问题在于DataSource结构体本身的生命周期。 - neon64
1个回答

0

这将使您的示例编译:

pub struct DrawCommand<'a : 'b, 'b, R: Resources<'a>> {
    pub data_source: &'b R::DataSource
}

然而,我发现创建一个更简单的示例非常困难。据我所知,您之所以有问题,是因为您声明将持有对具有引用的项的引用,并且这两个引用需要具有共同的生命周期('a)。通过其他生命周期的某种组合,实际上是不可能的。

添加第二个生命周期允许引用DataSourceDataSource本身的引用不同。

我仍然会尝试创建一个更简化的示例。


非常感谢,这正是我正在寻找的!我确实认为这是与数据源引用的生命周期有关的问题,需要将其指向的数据不同,但我不知道如何实现。具体来说:我不知道'a: 'b语法。我假设这声明了'a必须大于或等于'b - neon64
@neon64 很高兴能帮忙,尽管我仍然感到不满意,因为我无法制作一个更小的示例来演示核心问题。这个问题简洁地描述了所讨论的生命周期语法 - Shepmaster

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接