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.