Functions with limited lifetime

If my understanding is correct a function pointer is in some ways similar to a raw data pointer. Whereas a data pointer usually tells you what type of data it is pointing to, a function pointer tells you how to call the code at the location it points to. Unlike a data pointer, a function pointer cannot be dereferenced as it does not point to any meaningful data. The 'data' (actually code) a function pointer points to can only be accessed through calling the function pointer. Accessing data pointed to by a raw data pointer (*const T, *mut T) is considered unsafe as the lifetime of the data pointed to is unknown, the pointer might be dangling or otherwise invalid. However calling a function pointer is not unsafe, even though function pointers (obtained through FFI) can be(come) dangling or invalid as well. For example:

  1. Load a dynamic library
  2. Obtain a function pointer to a function from this library
  3. Unload the library
  4. Call the function pointer
  5. Segmentation fault/access violation

For some reason function pointers are always assumed to point to valid static memory. Even if you were to design the dynamic library loading code in such a way that it would only return a reference to a function pointer whose lifetime is restricted by the lifetime of library, it does not matter. Function pointers, just like raw data pointers and most primitive types implement Copy. The only workaround I see is to wrap function pointers in closures since closures are not Copy. However, closures are not function pointers. They cannot be used in place of function pointers, they do not have the notion of unsafety (unsafe keyword) and no external linkage, among other differences.

TL;DR: A raw pointer is to a reference as a function pointer is to ...? A closure? But not really.

Calling an unsafe function pointer (written unsafe fn()) is unsafe.

This is unsafe (among other issues, loading an arbitrary dynamic library can run arbitrary code).

I think any gaps in the current state of affairs can be bridged by user-defined types. (A closure is essentially just an unnamed struct which implements FnOnce.)