2

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?

  • Why not pass in the value of `devh` into the `new` function instead of calling it there? – Optimistic Peach Jul 01 '20 at 13:31
  • I guess that would be possible but the point of the camera-type is to abstract those details away. Eventually, I would like to add interfaces to other libraries - therefore it would make most sense to hide the library-specific code in the implementation. – AntonMølbjerg Jul 01 '20 at 13:34
  • Perhaps [How to store a reference without having to deal with lifetimes?](https://stackoverflow.com/q/49300618/155423) shows one solution, using the Rental crate – Shepmaster Jul 01 '20 at 13:38
  • 1
    I'm still learning Rust but I'm not using references in my struct or the new function - specifically because of these other questions. – AntonMølbjerg Jul 01 '20 at 13:43
  • *I'm not using references in my struct* — yes, you are. `<'a>` means that your structs contain one or more references with the lifetime `'a`. Every single member of your `UvcCamera` contains at least one reference. – Shepmaster Jul 01 '20 at 13:48
  • 2
    Ah thanks! I did not realize that. Then it makes much more sense and I guess I can use the suggested posts to find the answers I need. – AntonMølbjerg Jul 01 '20 at 13:56

0 Answers0