Reputation: 7902
I want to interface with a USB device so I'm using libusb. I have a function that returns a libusb::Device
of the device I am interested in:
pub fn obtain_device() -> Device<'static> {
let context: Context = libusb::Context::new().unwrap();
let option: Device = context
.devices()
.unwrap()
.iter()
.find(|d| d.device_descriptor().unwrap().vendor_id() == 0x0bda)
.unwrap();
option
}
However this does not compile:
error[E0515]: cannot return value referencing local variable `context`
--> src/usb/mod.rs:19:5
|
18 | let option: Device = context.devices().unwrap().iter().find(|d| d.device_descriptor().unwrap().vendor_id() == 0x0bda).unwrap();
| ------- `context` is borrowed here
19 | option
| ^^^^^^ returns a value referencing data owned by the current function
In my understanding it's the context
object that it causing the issue even though that is not the object I return from the function. Looking at the definition of libusb::Device
/// A reference to a USB device.
pub struct Device<'a> {
context: PhantomData<&'a Context>,
device: *mut libusb_device,
}
It contains a reference to a Context
which I assume is the cause of the compilation failure. But I am unsure of how I could return a Device
from the function or am I not thinking correctly in how this should be coded. What if I want to pass the Device
object to other functions?
Upvotes: 1
Views: 184
Reputation: 58825
Based on the definition of Context::devices
, the lifetime 'a
in Device<'a>
is bound to the scope of the context, and will become invalid (i.e. would be a dangling pointer) if the Context
goes out of scope. That is, the crate does not just wrap some pointer to a device that will live forever, but access the device "through" the context.
One way to deal with this would be to make the Context
static, so it lives for the entire duration of your program. You can do that with lazy_static
:
use lazy_static::lazy_static;
use libusb::{Context, Device};
lazy_static! {
static ref CONTEXT: Context = Context::new().unwrap();
}
pub fn obtain_device() -> Device<'static> {
CONTEXT
.devices()
.unwrap()
.iter()
.find(|d| d.device_descriptor().unwrap().vendor_id() == 0x0bda)
.unwrap()
}
You could also turn your function into a method of a struct, which owns the Context
.
pub struct MyDeviceContext {
context: libusb::Context,
}
impl MyDeviceContext {
pub fn new(context: libusb::Context) -> Self {
Self { context }
}
pub fn obtain_device(&self) -> Device<'_> {
self.context
.devices()
.unwrap()
.iter()
.find(|d| d.device_descriptor().unwrap().vendor_id() == 0x0bda)
.unwrap()
}
}
The device lifetime is no longer 'static
, but is usable as long as the MyDeviceContext
is still in scope.
Upvotes: 1