The opened DynamicLibrary handle is closed when dropped. If you don't keep it around, your function pointer pointing into the library becomes dangling.
You probably only want to open the library once. Perhaps do that in main() or put it in a shared cache (e.g. a HashMap wrapped in a lazy_static!) ensuring that any symbols obtained from the library remain valid for the duration of the program.
Also, consider using libloading, which associates lifetimes with libs and symbols, so it helps prevent this particular kind of error.
If you need hot reloading, then there's probably no simple way, I'd say. Once you reload the library, the function pointers are invalidated again. So you need to refresh any stored function pointers any time you reload the library. That sounds error-prone to me, so what I'd actually suggest is instead of storing function pointers, store the library handles only – even if they are stored in a global cache, you could just swap them out when you need a reload. Then, you could get one-off function pointers each time you want call one, which would ensure no dangling pointers.
Yes, the situation with dynamic loading in Rust is quite disappointing: libloading is the best option out there, but even that lib is unsound (it offers a Deref impl for extern fn targets, but since those are Copy, one can extract them out of the lifetime bound).
Here is a prototype of how libloading ought to be changed to offer a sound (lifetime-wise) API: Rust Playground
I have integrated a basic memoization strategy for it not to be too costly to load a symbol.
In your case, then, the simplest solution would be to have something like:
use ::parking_lot::Mutex;
static LIB: Mutex<Option<Library>> = Mutex::new(None);
So that your load function can LIB.lock().replace(Library::new(...).unwrap()) to "update" the loaded library;
Then, everytime you need something like "generate", you lock against library reloads with:
let lock_guard = LIB.lock();
let lib = lock_guard.as_ref().expect("Library not loaded yet");
and then can get, for instance, a Symbol<fn() -> u8> with lib.get(c!("generate")), that is guaranteed to be soundly usable while that lock is held / that lock_guard is in scope, while also being guaranteed to be using the "last" version of the loaded library (I am saying this because another solution would be for each "symbol" to hold an owned ref-counted instance of the library, but that would lead to symbols referring to out-of-date / stale libraries (it would be sound, but probably not what you want)).