0

I try to load a dynamic library and use its functions. For that I rely on libloading.

I struggle with the lifetimes.

libloading provides a Library struct, that allows me to load the dynamic library (Library::new) and to get individual functions from it (Library::get).

pub unsafe fn get<'lib, T>(
    &'lib self,
    symbol: &[u8]
) -> Result<Symbol<'lib, T>, Error>

This defines a lifetime 'lib referencing itself to make sure the library still exists when one calls its functions.

In my code I create struct PlugPlug that holds on the library and also its function (I don't want to call Library::get on every function call to avoid the overhead.). It exposes the library's function as a method (my_function).

As I need to define a lifetime for the Symbol holding the function from the lib, I've added 'dl to my struct and also adjusted load to use this lifetime. This is probablby the wrong approach, as I now can't call my_function after load due to mutable/immutable borrow conflict (see fn main()below).

use libloading::{Library, Symbol};

pub struct PlugPlug<'dl> {
    dyn_module: Option<Library>,
    my_func: Option<Symbol<'dl, fn(&str) -> PlugPlugErrorCode>>,
}

impl<'dl> PlugPlug<'dl> {
    pub fn new() -> PlugPlug<'dl> {
        PlugPlug {
            dyn_module: None,
            my_func: None,
        }
    }

    pub fn load(&'dl mut self, path: &Path) -> Result<(), String> {
        // load dynamic library
        let lib = unsafe { Library::new(path) }.map_err(|e| e.to_string())?;
        self.dyn_module = Some(lib);

        // load function from library
        unsafe {
            self.my_func = self
                .dyn_module
                .as_ref()
                .and_then(|lib| lib.get(b"THE_FUNCTION").ok());
        }
        if self.my_func.is_none()
        {
            return Err("Not all functions could be loaded".to_string());
        }
        Ok(())
    }

    pub fn my_function(&self, param: &str) -> Result<PlugPlugErrorCode, String> {
        match &self.my_func {
            Some(func) => {
                let result = unsafe { func(param) };
                Ok(result)
            }
            None => Err("Extension my_function not loaded".to_string()),
        }
    }
}

fn main() {
    let mut plugplug = PlugPlug::new();
    plugplug.load(Path::new(&"libpath")); // <-- mutable borrow
    let ext = plugplug.my_function("THE_PARAMETER"); // <-- immutable borrow E0202
}

Even wrapping the mutable borrow into its own context doesn't help:

fn main() {
    let mut plugplug;
    {
        plugplug = PlugPlug::new();
        plugplug.load(Path::new(&"libpath"));
    }
    let ext = plugplug.my_function("THE_PARAMETER"); // E0202
}

In the end, I'd want to use my struct's lifetime as the lifetime for the Symbol I retrieve via get, as my struct holds the library and when my struct goes away the library does, too.

How do I solve this? And why do I run into the lifetime problems with my approach?

MerlinDE
  • 195
  • 1
  • 11

0 Answers0