Struct initialization depends on moving borrowed value

I’m trying to initialize a struct like the following:

struct UsbCommandPort<'a> {
    serial: SerialPort<'a, UsbBus<USB>>,
    usb_dev: UsbDevice<'a, UsbBus<USB>>,
}

impl UsbCommandPort<'_> {
    pub fn new() -> Self
    {
        let usb: USB = USB::new();
        let usb_bus: UsbBusAllocator<UsbBus<USB>> = UsbBus::new(usb);

        let serial:SerialPort<'_, UsbBus<USB>> = SerialPort::new(&usb_bus);

        let usb_dev:UsbDevice<'_, UsbBus<USB>> = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
            .device_class(usbd_serial::USB_CLASS_CDC)
            .build();

        UsbCommandPort { 
            serial: serial, 
            usb_dev: usb_dev,
        }
    }

Both SerialPort::new and UsbDeviceBuilder::new take a reference to usb_bus.

This function fails to compile with the error

error[E0515]: cannot return value referencing local variable `usb_bus`

This makes sense, as usb_bus will cease to exist when the function returns, and the references will be dangling.

To prevent this from happening, I’d like to transfer ownership of usb_bus to the struct. So I add it as as a member variable:

struct UsbCommandPort<'a> {
    usb_bus: UsbBusAllocator<UsbBus<USB>>,
    serial: SerialPort<'a, UsbBus<USB>>,
    usb_dev: UsbDevice<'a, UsbBus<USB>>,
}

impl UsbCommandPort<'_> {
    pub fn new() -> Self
    {
        let usb: USB = USB::new();
        let usb_bus: UsbBusAllocator<UsbBus<USB>> = UsbBus::new(usb);

        let serial:SerialPort<'_, UsbBus<USB>> = SerialPort::new(&usb_bus);

        let usb_dev:UsbDevice<'_, UsbBus<USB>> = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
            .device_class(usbd_serial::USB_CLASS_CDC)
            .build();

        UsbCommandPort {
            usb_bus: usb_bus, 
            serial: serial, 
            usb_dev: usb_dev,
        }
    }

However this also fails to compile. The previous error is still there:

error[E0515]: cannot return value referencing local variable `usb_bus`

and now there is a new error:

error[E0505]: cannot move out of `usb_bus` because it is borrowed

I cannot transfer ownership of usb_bus to the struct because it is already borrowed. However, I cannot create the struct without initializing serial and usb_dev which require the borrows.

Can I somehow incrementially initalize the struct, by first assigning usb_bus, then update it by assigning serial and usb_dev with references to the struct-owned usb_bus?

>Solution :

Since serial and usb_dev both have references to usb_bus, Rust’s borrow checker prevents you from structuring the code the way you’re attempting to.

However, you can use Rust’s ownership model to work around this issue. One way is to use Rc or Arc to enable multiple ownership of the usb_bus within the struct. Here’s how you can modify your code to achieve this:

use std::sync::Arc;

struct UsbCommandPort<'a> {
    usb_bus: Arc<UsbBusAllocator<UsbBus<USB>>>,
    serial: SerialPort<'a, UsbBus<USB>>,
    usb_dev: UsbDevice<'a, UsbBus<USB>>,
}

impl UsbCommandPort<'_> {
    pub fn new() -> Self {
        let usb: USB = USB::new();
        let usb_bus: Arc<UsbBusAllocator<UsbBus<USB>>> = Arc::new(UsbBus::new(usb));

        let serial: SerialPort<'_, UsbBus<USB>> = SerialPort::new(&usb_bus);

        let usb_dev: UsbDevice<'_, UsbBus<USB>> = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
            .device_class(usbd_serial::USB_CLASS_CDC)
            .build();

        UsbCommandPort {
            usb_bus: usb_bus.clone(),
            serial,
            usb_dev,
        }
    }
}

By wrapping usb_bus in an Arc, you enable multiple ownership. The clone() method on Arc is used to create new references, which will be used in both serial and usb_dev. This way, ownership issues related to borrowing should be resolved. Just keep in mind that Arc introduces some overhead due to reference counting, so be sure to use it only when necessary.

Leave a Reply