How to create a singleton for a DLL/so

I'm creating some experimental bindings for IUP (GUI lib'y).

I can successfully create an object that represents the lib'y. However, I only want one application-global object for this purpose, something like this:

// iup: Iup; // iup needs to be a static Iup object
pub fn get_iup() -> &Iup {
   // if an Iup hasn't been created, do: iup = Iup::new();
   &iup; // return a reference to the single iup object
}

Unfortunately, I can't work out how to do the above because I keep hitting lifetime issues.

Here's the code I have so far:

use crate::{xerr, xerror::{xerror, XResult}}; // just my own result type nothing fancy
use lazy_static::lazy_static;
use libloading::{Library, Symbol};
use std::env;
use std::ffi::CStr;
use std::os::raw::c_char;
use std::path::PathBuf;
use std::ptr;
use std::str;

lazy_static! {
    pub(crate) static ref IUP: Library = Library::new(dll()).expect("Failed to find IUP library");
}

fn dll() -> PathBuf {
    let exe = env::current_exe().expect("Failed to find exe's path");
    let root = exe.parent().expect("Failed to find location of IUP library");
    let mut root = root.to_path_buf();
    if cfg!(windows) {
        root.push("iup/windows/iup.dll");
    } else {
        root.push("iup/linux/libiup.so");
    };
    root
}

pub struct Iup<'a> {
    _close: Symbol<'a, SigVrV>, 
    _version: Symbol<'a, SigVrpC>,
    _set_global: Symbol<'a, SigpCpCrV>,
}

impl<'a> Iup<'a> {
    pub fn new() -> XResult<Iup<'a>> {
        let iup_open: Symbol<'a, SigpIpppCrI> =
            unsafe { IUP.get(b"IupOpen\0").unwrap() };
        if iup_open(ptr::null(), ptr::null()) != NOERROR {
            xerr!("Failed to open IUP library");
        }
        let set_global: Symbol<SigpCpCrV> = unsafe {
            IUP.get(b"IupSetGlobal\0").unwrap()
        };
        set_global(c_from_str(UTF8MODE), c_from_str(YES));
        Ok(Iup {
            _close: unsafe { IUP.get(b"IupClose\0").unwrap() },
            _version: unsafe { IUP.get(b"IupVersion\0").unwrap() },
            _set_global: set_global,
        })
    }

    pub fn set_global(self, name: &str, value: &str) {
        (self._set_global)(c_from_str(&name), c_from_str(&value));
    }

    pub fn version(self) -> String {
        match c_to_string((self._version)()) {
            Ok(v) => v,
            Err(_) => "?".to_string(),
        }
    }
}

impl<'a> Drop for Iup<'a> {
    fn drop(&mut self) {
        (self._close)();
    }
}

fn c_to_string(p: *const c_char) -> XResult<String> {
    let c: &CStr = unsafe { CStr::from_ptr(p) };
    let s: &str = c.to_str()?;
    Ok(s.to_owned())
}

fn c_from_str(s: &str) -> *const c_char {
    unsafe { &*(s as *const _ as *const [c_char]) }.as_ptr()
}

#[repr(C)] pub struct Ihandle { _private: [u8; 0] }
pub(crate) type SigpIpppCrI = extern "C" fn(*const i32, *const *const *const c_char) -> i32;
pub(crate) type SigpCpCrV = extern "C" fn(*const c_char, *const c_char);
pub(crate) type SigVrV = extern "C" fn();
pub(crate) type SigVrpC = extern "C" fn() -> *const c_char;
pub const ERROR: i32 = 1;
pub const NOERROR: i32 = 0;
pub const YES: &str = "YES";
const UTF8MODE: &str = "UTF8MODE";

Is it possible to create a get_iup() function that will return the singleton with a static lifetime so that I can use it everywhere?

There’s a few ways to do this, depending on how important it is to cleanly tear down the library:

  • If there’s no cleanup code that needs to run at program exit, you can use Box::leak to generate a &'static Iup reference you can store.
  • You can store and return an Rc<Iup> or an Arc<Iup> instead of a reference, which lets you have a chance to deallocate it via try_unwrap when you’re ready to shut things down, but only if everything has given back its handles already.
  • You can write a function with_iup(_:impl FnMut(&Iup)) that takes a closure that will be given the reference as a parameter. This is the most awkward to use, but statically guarantees that the reference never gets copied out of the closure.

Thanks for your reply.
I made some progress but now have a new problem.
I finally figured out the syntax for lazy static, so I now have an IUP static singleton object.
However, as soon as I use it I get moving and borrowing errors.
Below is the application code that gives the error and below that is the library code:

// helloiup.rs
use iup::IUP;

fn main() {
    println!("Hello, IUP v{}", IUP.version());
    IUP.close();
}

Here're the errors this produces:

   Compiling helloiup v0.1.0 (/home/mark/app/rs/helloiup)
error[E0507]: cannot move out of dereference of `iup::iup::IUP`
 --> src/main.rs:6:32
  |
6 |     println!("Hello, IUP v{}", IUP.version());
  |                                ^^^ move occurs because value has type `iup::iup::Iup<'_>`, which does not implement the `Copy` trait

error[E0596]: cannot borrow data in a dereference of `iup::iup::IUP` as mutable
 --> src/main.rs:7:5
  |
7 |     IUP.close();
  |     ^^^ cannot borrow as mutable

And the library (with most parts already shown elided):

// Same imports as before

lazy_static! {
    pub(crate) static ref IUP_LIB: Library = Library::new(dll()).expect(
        "Failed to find IUP library");
    pub static ref IUP: Iup<'static> = Iup::new().expect(
        "Failed to create IUP object"); // NEW
}

fn dll() -> PathBuf { // ... Same as before

pub struct Iup<'a> {
    closed: bool, // NEW
    _close: Symbol<'a, SigVrV>, 
    _version: Symbol<'a, SigVrpC>,
    _set_global: Symbol<'a, SigpCpCrV>,
}

impl<'a> Iup<'a> {
    fn new() -> XResult<Iup<'a>> {
        let iup_open: Symbol<SigpIpppCrI> = unsafe {
            IUP_LIB.get(b"IupOpen\0").unwrap()
        };
        if iup_open(ptr::null(), ptr::null()) != NOERROR {
            xerr!("Failed to open IUP library");
        }
        let set_global: Symbol<SigpCpCrV> = unsafe {
            IUP_LIB.get(b"IupSetGlobal\0").unwrap()
        };
        set_global(c_from_str(UTF8MODE), c_from_str(YES));
        Ok(Iup {
            closed: false, // NEW
            _close: unsafe { IUP_LIB.get(b"IupClose\0").unwrap() },
            _version: unsafe { IUP_LIB.get(b"IupVersion\0").unwrap() },
            _set_global: set_global,
        })
    }

    pub fn close(&mut self) { // NEW: Must be called at application termination
        if !self.closed {
            (self._close)();
            self.closed = true;
        }
    }

    pub fn set_global(self, name: &str, value: &str) {
        (self._set_global)(c_from_str(&name), c_from_str(&value));
    }

    pub fn version(self) -> String {
        match c_to_string((self._version)()) {
            Ok(v) => v,
            Err(_) => "0.0".to_string(),
        }
    }
}

// Rest as before

I don't mind making IUP private and having a public get_iup() function that returned a reference to it. (I only need the mut because of close, but could do without it.)

version is defined to consume the Iup structure. You probably meant for it to take only a reference:

pub fn version(&self) -> String { /* ... */ }

Thank you!
I'd done the same mistake for another method too. Fixed both and now it works. (I also took out the closed bool so the Iup object is just a bunch of functions.)

(I find that -- apart from mistakes -- my commonest problem with Rust is just figuring out the syntax.)

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.