How do I get Rust to enforce a lifetime for a *mut in unsafe code?

Viewed 190

I would like to call the C function libusb1_sys::libusb_set_pollfd_notifiers() from Rust, providing callbacks and context. The C function is:

void libusb_set_pollfd_notifiers(
    libusb_context *            ctx,
    libusb_pollfd_added_cb      added_cb,
    libusb_pollfd_removed_cb    removed_cb,
    void *                      user_data 
);

This follows the traditional method for "closures" in C where user_data provides context for the callback.

The following code compiles and runs, but as far as I can tell is wrong:

use libusb1_sys::libusb_pollfd;
use rusb::UsbContext;

extern "system" fn usb_source_added_cb(
    _fd: libc::c_int,
    _events: libc::c_short,
    _user_data: *mut libc::c_void,
) {
    println!("USB source added");
}

extern "system" fn usb_source_removed_cb(
    _fd: libc::c_int,
    _user_data: *mut libc::c_void,
) {
    println!("USB source removed");
}

struct UsbFdChannel {
    sender:  std::sync::mpsc::Sender<libc::c_int>,
    context: rusb::Context,
}

impl UsbFdChannel {
    fn new() -> (Self, std::sync::mpsc::Receiver<libc::c_int>) {
        let context = rusb::Context::new().unwrap();
        // This gives me a `*mut libusb_context`
        let raw_context = context.as_raw();

        let (mut sender, receiver) = std::sync::mpsc::channel();

        unsafe {
            libusb1_sys::libusb_set_pollfd_notifiers(
                raw_context,
                usb_source_added_cb,
                usb_source_removed_cb,
                &mut sender as *mut _ as *mut libc::c_void,
            );
        }

        (Self { sender, context }, receiver)
    }
}

fn main() {
    let _usb_channel = UsbFdChannel::new();
}

I think it's wrong because once UsbFdChannel::new() returns, the mut * to sender is no longer valid, but libusb's context is still able to use it. Am I correct?

What I would like is for this incorrect code to not compile. If possible, I'd like this entire class of incorrect code to not compile. I think what I want is to associate a lifetime with sender, or possibly with &mut sender, that the compiler will then enforce. Or maybe I want to "trick" rust into thinking that the lifetime of *mut sender is bounded by that of &mut sender...?

If I achieve that, then I can resolve the incorrectness however I can - by having UsbFdChannel take a reference, or create a reference counted/boxed/both thing, or whatever. The important thing to me (for both solving this particular problem and for learning how to write better Rust) is that I can identify and declare the ownership problem first, and then solve that problem second.

I considered using std::marker::PhantomData, but since UsbFdChannel is not parameterised by a type, I don't see how to tell it what member the PhantomData's lifetime would correspond to. I also considered using some kind of non-stack allocation and reference counting, but I still want to know (inasmuch as Rust can tell me) that I've solved the problem. How do I do that?

0 Answers
Related