Struct initialization

I'm building an easy to use interface for the libuvc camera-library in Rust. I want to define a Camera type that is easy to set up. I'm using libuvc-rs as an interface to the libuvc c-library. Setting up a camera using libuvc requires calling a number of functions that each return handles used to perform different functions in the uvc library. My struct UvcCamera, therefore, contains one of each of these types for operation:

pub struct UvcCamera<'a> {
    ctx: uvc::Context<'a>,
    dev: uvc::Device<'a>,
    devh: uvc::DeviceHandle<'a>,
    streamh: uvc::StreamHandle<'a>,
}

I cannot create a function that legally initializes these variables. Importantly, the function doesn't return a reference but an owned object. The struct members are also not references but owned objects. No matter the approach I choose, I end up with some kind of borrowing error (and I have searched the internet endlessly for similar problems but without luck). The most straightforward implementation is shown below:

impl<'a> UvcCamera<'a> {

    pub fn new() -> uvc::Result<UvcCamera<'a>> {
        let serial = UvcCamera::serial_number_from_uid(1)?;
        let ctx: uvc::Context<'a> = uvc::Context::new()?;
        let dev = ctx.find_device(None, None, Some(&serial))?;
        let devh = dev.open()?;

        let format = uvc::StreamFormat {
            width: 640,
            height: 480,
            fps: 60,
            format: uvc::FrameFormat::MJPEG,
        };

        let streamh = devh.get_stream_handle_with_format(format)?;
        Ok(UvcCamera { ctx, dev, devh, streamh })
    }

This results in the error

error[E0515]: cannot return value referencing local variable `devh`
  --> src/camera.rs:36:9
   |
35 |         let streamh = devh.get_stream_handle_with_format(format)?;
   |                       ---- `devh` is borrowed here
36 |         Ok(UvcCamera { ctx, dev, devh, streamh })
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function

error[E0515]: cannot return value referencing local variable `dev`
  --> src/camera.rs:36:9
   |
26 |         let devh = dev.open()?;
   |                    --- `dev` is borrowed here
...
36 |         Ok(UvcCamera { ctx, dev, devh, streamh })
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function

error[E0515]: cannot return value referencing local variable `ctx`
  --> src/camera.rs:36:9
   |
25 |         let dev = ctx.find_device(None, None, Some(&serial))?;
   |                   --- `ctx` is borrowed here
...
36 |         Ok(UvcCamera { ctx, dev, devh, streamh })
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function

error[E0505]: cannot move out of `ctx` because it is borrowed
  --> src/camera.rs:36:24
   |
20 | impl<'a> UvcCamera<'a> {
   |      -- lifetime `'a` defined here
...
25 |         let dev = ctx.find_device(None, None, Some(&serial))?;
   |                   --- borrow of `ctx` occurs here
...
36 |         Ok(UvcCamera { ctx, dev, devh, streamh })
   |         ---------------^^^-----------------------
   |         |              |
   |         |              move out of `ctx` occurs here
   |         returning this value requires that `ctx` is borrowed for `'a`

error[E0505]: cannot move out of `dev` because it is borrowed
  --> src/camera.rs:36:29
   |
20 | impl<'a> UvcCamera<'a> {
   |      -- lifetime `'a` defined here
...
26 |         let devh = dev.open()?;
   |                    --- borrow of `dev` occurs here
...
36 |         Ok(UvcCamera { ctx, dev, devh, streamh })
   |         --------------------^^^------------------
   |         |                   |
   |         |                   move out of `dev` occurs here
   |         returning this value requires that `dev` is borrowed for `'a`

error[E0505]: cannot move out of `devh` because it is borrowed
  --> src/camera.rs:36:34
   |
20 | impl<'a> UvcCamera<'a> {
   |      -- lifetime `'a` defined here
...
35 |         let streamh = devh.get_stream_handle_with_format(format)?;
   |                       ---- borrow of `devh` occurs here
36 |         Ok(UvcCamera { ctx, dev, devh, streamh })
   |         -------------------------^^^^------------
   |         |                        |
   |         |                        move out of `devh` occurs here
   |         returning this value requires that `devh` is borrowed for `'a`

error[E0515]: cannot return value referencing local variable `devh`
  --> src/camera.rs:36:9
   |
35 |         let streamh = devh.get_stream_handle_with_format(format)?;
   |                       ---- `devh` is borrowed here
36 |         Ok(UvcCamera { ctx, dev, devh, streamh })
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function

error[E0515]: cannot return value referencing local variable `dev`
  --> src/camera.rs:36:9
   |
26 |         let devh = dev.open()?;
   |                    --- `dev` is borrowed here
...
36 |         Ok(UvcCamera { ctx, dev, devh, streamh })
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function

error[E0515]: cannot return value referencing local variable `ctx`
  --> src/camera.rs:36:9
   |
25 |         let dev = ctx.find_device(None, None, Some(&serial))?;
   |                   --- `ctx` is borrowed here
...
36 |         Ok(UvcCamera { ctx, dev, devh, streamh })
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function

error[E0505]: cannot move out of `ctx` because it is borrowed
  --> src/camera.rs:36:24
   |
20 | impl<'a> UvcCamera<'a> {
   |      -- lifetime `'a` defined here
...
25 |         let dev = ctx.find_device(None, None, Some(&serial))?;
   |                   --- borrow of `ctx` occurs here
...
36 |         Ok(UvcCamera { ctx, dev, devh, streamh })
   |         ---------------^^^-----------------------
   |         |              |
   |         |              move out of `ctx` occurs here
   |         returning this value requires that `ctx` is borrowed for `'a`

error[E0505]: cannot move out of `dev` because it is borrowed
  --> src/camera.rs:36:29
   |
20 | impl<'a> UvcCamera<'a> {
   |      -- lifetime `'a` defined here
...
26 |         let devh = dev.open()?;
   |                    --- borrow of `dev` occurs here
...
36 |         Ok(UvcCamera { ctx, dev, devh, streamh })
   |         --------------------^^^------------------
   |         |                   |
   |         |                   move out of `dev` occurs here
   |         returning this value requires that `dev` is borrowed for `'a`

error[E0505]: cannot move out of `devh` because it is borrowed
  --> src/camera.rs:36:34
   |
20 | impl<'a> UvcCamera<'a> {
   |      -- lifetime `'a` defined here
...
35 |         let streamh = devh.get_stream_handle_with_format(format)?;
   |                       ---- borrow of `devh` occurs here
36 |         Ok(UvcCamera { ctx, dev, devh, streamh })
   |         -------------------------^^^^------------
   |         |                        |
   |         |                        move out of `devh` occurs here
   |         returning this value requires that `devh` is borrowed for `'a`

Importantly, all the libuvc types implement Drop (logical since they hold references that need to be freed) and therefore can't derive Copy. Is there any way constructing this kind of type is possible?

In general you can’t create a lifetime out of nothing without unsafe. (At least I’m pretty sure that’s true.)

What does UvcCamera::from_serial_number look like?

The two device related variables are tied to the context lifetime. I think your best bet would be to create the context outside of the new function and then pass it into the function. Then the two dev variables can be created from the context and have the same lifetime.

That’s assuming from_serial_number can be changed to be tied to the context lifetime.

1 Like

<'a> on struct means it's a temporary view into another struct, and such struct cannot be created from nothing, and cannot exist on its own.

You can't return something with a lifetime from a function that doesn't take that data in as an argument. So it's impossible to create UvcCamera<'a> inside a function if that function doesn't take some other struct with <'a> or &'a reference that already contains uvc::Context<'a> etc.

If uvc is your crate, then don't use references in structs. They are not for storing things by reference. If you want to store things by reference, use Box or Arc instead.

Thank you very much for both of your replies - I think I understand the problem much better now. It seems that the best solution for me is to not use the wrapper library providing the uvc contexts and so forth but instead building my own wrapper (or modifying the existing one) which doesn't use references in this manner.