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?