FFI Api lifetime problem


#1

Hi!

I am currently trying to write a GUI application with gtk-rs, which uses one of my own libraries, I wrote a year ago.
Now I am hitting a problem with gtk-rs, where action callbacks can be connected via closures, but these require a
’static lifetime bound.

Here a link to an example, which is pretty much a stripped down version of the problem: https://play.rust-lang.org/?gist=23c67192671c8d5d7abfd56392be1ba5&version=nightly

As you can see the I am trying to execute some action on my device, which is bound to the lifetime of the global API context via. a PhantomData in its struct definition.

Now the question: Can I work around this in my application, or do I have to change my previously written library, to use
refcounting instead of lifetimes?


#2

You can put api into the RefCell, not &api. Then call get_device directly on clones of the Rc where you need it.


#3

I’m writing a gtk-rs application right now for viewing some specific weather data. I called it sonde. I have a large struct called AppContext that I put all the GUI and program state I might need to reference into. Then during program startup I put the AppContext in an Rc<RefCell<AppContext>> smart pointer. Then I have a GUI struct that I pass the smart pointer into during construction.

In my GUI constructor I make the callbacks and use clones exactly as @vitalyd suggested. Here is a link to the point in the library where everything is set up.

That’s as clean as I’ve been able to get it with gtk-rs. It works really well once you get the hang of the patterns involved. It took me a while to figure all this out though, but I don’t do much GUI programming.


#4

Thanks for the answers!

But still I can not get the compiler to be happy. I just tried the approach with the context object,
but I can not put both my API and Device objects in there, because they reference each other.

Is there some solution to this problem (I heard of it, but always managed to work around it before by design).


#5

I suspect you’re running into self-referential structs issue - your context object owns the API and Device but the API holds a reference to the Device, leading to a struct that has a reference to a part of itself (this isn’t allowed in Rust). There are some ways and libs to work around this, but lets confirm that is indeed your issue.


#6

It is indeed (but the other way around). The device is holding a reference to the API somewhere deep nested inside (by a 3rd party library)


#7

Take a look at the rental crate to see if it can help with the self-referencing aspect. Alternatively, is it possible to store just the Device in the context and access API from it when needed?


#8

Okay I`ll take a look at the rental crate.

I do not know what you mean by just storing the device in the context. That`s exactly how I had done it before (I think?).
The problem is, that I am moving a device into the static closure (via a Rc), but the device still has a reference with a not static lifetime bound to the API outside the closure, so that wont work.

I also already tried to put both the API and device behind Rc`s, and move them both inside the closure, but because I instantiated the device before, the compiler can not really figure out, that the reference will stay valid (because the Rc owned by the closure will gurantee the API lifetime over the lifetime of the closure).


#9

Can Device own the API instead of holding a reference? Maybe it can own an Rc of the API if you want to share ownership over it. Or you don’t have control over this? The bottom line is you won’t be able to move anything into a closure expecting 'static if it has any references.


#10

Hmm yeah the problem is, that the API creates devices for USB nodes with a open_XXX method, and injects a reference of itself into the device struct.

I think I will try to fork the library, and change it, to use Rc`s for referencing the API instance.

Thanks for the help!