0

I am working on a Rust project using rusb to interact with USB devices. I'm facing a lifetime management issue where I cannot return a struct containing a reference due to it referencing a local variable within a nested loop. Here's the simplified code that leads to the issue:

The below code snippet provided is part of the rusb crate, which is a third-party library for handling USB communications in Rust.

pub struct InterfaceDescriptors<'a> {
    iter: slice::Iter<'a, libusb_interface_descriptor>,
}

impl<'a> Iterator for InterfaceDescriptors<'a> {
    type Item = InterfaceDescriptor<'a>;

    fn next(&mut self) -> Option<InterfaceDescriptor<'a>> {
        self.iter.next().map(|descriptor| InterfaceDescriptor { descriptor })
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.iter.size_hint()
    }
}

pub struct InterfaceDescriptor<'a> {
    descriptor: &'a libusb_interface_descriptor,
}

Here is the code snippet from my crate:

use rusb::{Device, Context, UsbContext};

pub struct ABC<'a> {
    /// Descriptor of the USB interface.
    usb_interface_descriptor: &'a rusb::InterfaceDescriptor<'a>,
}

impl<'a> ABC<'a> {
    pub fn find_compatible_device(
        usb_device: &'a Device<Context>,
    ) -> anyhow::Result<Option<ABC<'a>>, rusb::Error> {
        let dev_descriptor = usb_device.device_descriptor()?;
        for cfg_index in 0..dev_descriptor.num_configurations() {
            let cfg_descriptor = usb_device.config_descriptor(cfg_index)?;
            for interface in cfg_descriptor.interfaces() {
                for descriptor in interface.descriptors() {
                    // <!------ Error occurs here  ------>
                    return Ok(Some(ABC {
                        usb_interface_descriptor: &descriptor,
                    }));
                }
            }
        }
        Ok(None)
    }
}

The usb_interface_descriptor cannot be cloned. I understand that cfg_descriptor is dropped at the end of the loop, making any references to its contents invalid.

I am attempting to return an instance of ABC that holds a reference to an InterfaceDescriptor. However, I get the following compilation error:

error[E0515]: cannot return value referencing local variable `cfg_descriptor`
  --> src/test.rs:20:28
   |
17 |               for interface in cfg_descriptor.interfaces() {
   |                                -------------- `cfg_descriptor` is borrowed here
...
20 |                       return Ok(Some(ABC {
   |  ____________________________^
21 | |                         usb_interface_descriptor: &descriptor,
22 | |                     }));
   | |_______________________^ returns a value referencing data owned by the current function

error[E0515]: cannot return value referencing local variable `descriptor`
  --> src/test.rs:20:28
   |
20 |                       return Ok(Some(ABC {
   |  ____________________________^
21 | |                         usb_interface_descriptor: &descriptor,
   | |                                                   ----------- `descriptor` is borrowed here
22 | |                     }));
   | |_______________________^ returns a value referencing data owned by the current function

What would be the best way to refactor this code to avoid the lifetime issue while still returning necessary data from the function?

2
  • Why do you store a reference &'a libusb_interface_descriptor in ABC instead of libusb_interface_descriptor directly? Commented May 26, 2024 at 17:10
  • Side note: there are legitimate uses for &'a Foo<'a> but they are few and far between, so whenever you're tempted to write it you're probably doing something wrong. Commented May 27, 2024 at 7:01

1 Answer 1

1

Looking at the definition

pub struct InterfaceDescriptor<'a> {
    descriptor: &'a libusb_interface_descriptor,
}

InterfaceDescriptor is nothing but a thin wrapper over a reference, so you do not gain much by trying to avoid copying it (via using a reference).

I would just define ABC to hold InterfaceDescriptor directly (not a reference to it).

Sign up to request clarification or add additional context in comments.

2 Comments

This is good advice. Unfortunately, rusb::InterfaceDescriptor does not implement Clone, which it should be able to do (it does not implement Drop). Someone should open an issue: github.com/a1ien/rusb/issues.
@trueequalsfalse there's nothing in the OP code that requires InterfaceDescriptor to be Clone. However if the OP wants ABC to be clonable, the usual solution would be to store a Rc<InterfaceDescriptor<'a>> or Arc<InterfaceDescriptor<'a>>.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.