I think in this case it’ll entirely depend upon what your FFI interface is doing and how it’s been designed. The problem of “how do I make my code reusable?” is applicable to most software development, but FFI does tend to add its own nuances and constraints (e.g. you’re limited to a primitive C-style interface, so common tools like generics are out).
One design which comes to mind is having a plugin architecture. Basically, if you’re trying to expose the same interface (e.g. different implementations of the same
Plugin trait) then you can have a
extern "C" fn() which will return a new boxed trait object or vtable. This would mean each implementation of the trait is in its own crate/dylib, with some sort of
register_plugin() function that creates a new trait object and gives Lua a pointer to it.
I don’t normally try to reuse an FFI interface itself, seeing as each function should only be 2 or 3 line shims for transforming arguments and delegating to the appropriate Rust function/struct method. Instead, I’ll typically try to extract common patterns and ways of doing things out into their own helpers crate/module and reuse that. I did a lot of FFI at work a while back and we managed to find some really nice ways of doing things (which I’ve since published as the ffi_helpers crate). I’m especially happy with our solution to spinning a Rust task off on a background thread that you can periodically poll, cancel, or retrieve the result/error from (kinda like the FFI version of
As a side note, I’m currently in the process of rewriting my Rust FFI guide (new version here). The main goal for it is to teach people how to solve exactly these sorts of problems, so it may be a helpful resource. The new version is in no way complete yet, so feedback/comments/complaints is always welcome.