I want to liberally use Library and Symbol from the libloading crate, to load and retrieve symbols from a plugin library that I or other programmers may create.
However, libloading::Library::new() drops and closes the library as soon as it goes out of scope. Similarly, it seems Symbols can't be called when they go out of scope (I tried and there is a core dump).
I could dynamically allocate them, but I don't want to use unsafe code if at all possible.
What is the best way to keep Library and Symbol around?
My point is that it takes away flexibility rather than add it.
One alternative solution is to just wrap each Library instance in an Arc<T>. That way you can use it as normal, pass it around like an owned value, and if/when you're done with a Library, you simply drop all bindings to that Arc<Library>.
I don't think there's anything wrong with using Box::leak here if you don't want it to be unloaded. Another similar option is mem::forget, or putting it in a global variable.
You already can't rely on unloading to work across platforms. Musl implements dlclose as no-op, macOS only supports unloading in certain cases and thread locals (which libstd uses) can throw a wrench in unloading too.
If you are using bindgen for converting your C header to Rust function definitions, you should check out the --dynamic-loading flag (or the dynamic_library_name() method if calling bindgen from build.rs).
This generates a struct which contains your libloading::Library and pointers for any functions you are binding to. That way you don't run into issues around lifetimes and use-after-frees.
For example, I can use the following header file...
// people.h
struct person {
const char name[16];
};
void print_name(struct person *p);
... and this is the output I get from bindgen --dynamic-loading people people.h: