Grouping an owner and a borrow together


#1
use std::marker::PhantomData;
struct Foo<'r> {
    l: PhantomData<&'r ()>,
}
struct Bar<'r> {
    l: PhantomData<&'r ()>,
}
impl<'r> Foo<'r> {
    fn create(&'r self) -> Bar<'r> {
        Bar { l: PhantomData }
    }
}
struct Test<'r> {
    f: Foo<'r>,
    b: Bar<'r>,
}
fn main() {
    let f = Foo{l: PhantomData};
    let b = f.create();
    let t = Test{f:f, b:b};
}

playground

The lifetime system is great, Foo can create a Bar and everything is checked at compile time. But what if we want to group them together? I could remove the lifetimes and use an Arc but that sort of defeats the purpose of the lifetimes.

What are the alternatives?


#2

I haven’t quite seen the case in this example, where both owner and borrow have a lifetime. Generally Foo owns the data, which means it doesn’t need it. In any case, this is made complicated by the need to avoid dangling pointers and the unspecified drop order of struct fields.

However, there are crates that allow you to build structs like this in a limited fashion: owning_ref and rental. The former is easier and works with a few common types, the latter requires macro boilerplate but can work with arbitrary derived types that have a lifetime.


#3

The use case is for my Vulkan wrapper https://github.com/MaikKlein/ash You have to load the .dll/.so, load the entry points, create an Instance and then from the instance you can create a Device.

Because I am using a life time I didn’t have to mark everything unsafe, because if the entry point goes out of scope and unloads the function pointers while instance/device are still alive we have a problem.

Though I just realized that I could just use mem::forget on the lib loader and let the user manually free the libloader. Or I can just use an Arc. I’ll have a look at owning-ref and rental to see if that helps me, thanks.


#4

If I remember correctly, the motivating use case for rental was symbols loaded from a library too.

As for Arcs, personally I think they’re a good choice for objects that are not typically created and disposed of quickly and in large numbers. (For example, rust-zmq made the context into Arc<RawContext> and keeps a reference to it in every socket).


#5

Author of rental here. Birkenfeld is right that this is exactly the kind of thing rental is for. I use it here in al-sys to load all the Symbols for OpenAL into a struct, stored alongside the Library that owns them, which sounds like exactly what you’re after. Let me know if you have any questions about it.