Wrapping a library with a global variable

I want to write a Rust wrapper for a C library that does something a little suboptimal:

static int fd = -1;

int foo_init() {
  fd = open("/dev/foo", ...);
  /* ... */
  return 0;
}

int foo_term() {
  if(fd != -1) {
    close(fd);
    fd = -1;
  }
}

/* .. a bunch of foo_ functions that issue ioctl's on the fd .. */

I can't change the C library. I'm used to wrapping handles in Rust wrappers, but I haven't encountered a one-init-per-process initialization-to-global-variable before.

My wrapper currently does something along the line of:

struct Foo { }
impl Foo {
  pub fn init() {
    let rc = unsafe { raw::foo_init() };
    if rc == -1 {
      panic!("Unable to initialize foo");
    }
  }
  // .. foo_ functions ..
}

Optimally I want to hide the need to explicitly call foo_init() from the user.

What's the least controversial way to do this? Is there some creative way to (mis)use OnceCell to only call foo_init() once?

1 Like

I don't know about OnceCell (which is nightly), but there is Once you could try:

use std::sync::Once;

static FOO_INIT: Once = Once::new();

struct Foo { }

impl Foo {
  pub fn init() {
    FOO_INIT.call_once(|| {
        let rc = unsafe { raw::foo_init() };
        if rc == -1 {
            panic!("Unable to initialize foo");
        }    
    });
  }
  // .. foo_ functions ..
}
2 Likes

Note you can put the static inside the function (so long as it's not generic, iirc?)

2 Likes

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.