Home > OS >  Rust lifetimes in async wrapper for sync code
Rust lifetimes in async wrapper for sync code

Time:07-03

I am trying to create a Stream using a camera with a blocking capture method. The blocking call is wrapped with blocking::unblock.

use futures::stream;
use rscam::{Camera, Config};

fn frame_stream() -> impl stream::Stream {
    let mut camera = Camera::new("/dev/video0").unwrap();

    camera.start(&Config {
        interval: (1, 30),
        resolution: (1280, 720),
        format: b"H264",
        ..Default::default()
    }).unwrap();

    stream::unfold(camera, |c| async move {
        let frame = blocking::unblock(|| c.capture().unwrap()).await;
        
        Some((frame, c))
    })
}

Compiling gives this error message:

error[E0373]: closure may outlive the current function, but it borrows `c`, which is owned by the current function
  --> src/lib.rs:15:39
   |
15 |         let frame = blocking::unblock(|| c.capture().unwrap()).await;
   |                                       ^^ - `c` is borrowed here
   |                                       |
   |                                       may outlive borrowed value `c`
   |
note: function requires argument type to outlive `'static`
  --> src/lib.rs:15:21
   |
15 |         let frame = blocking::unblock(|| c.capture().unwrap()).await;
   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: to force the closure to take ownership of `c` (and any other referenced variables), use the `move` keyword
   |
15 |         let frame = blocking::unblock(move || c.capture().unwrap()).await;
   |                                           

error[E0505]: cannot move out of `c` because it is borrowed
  --> src/lib.rs:17:22
   |
15 |         let frame = blocking::unblock(|| c.capture().unwrap()).await;
   |                     ------------------------------------------
   |                     |                 |  |
   |                     |                 |  borrow occurs due to use in closure
   |                     |                 borrow of `c` occurs here
   |                     argument requires that `c` is borrowed for `'static`
16 |         
17 |         Some((frame, c))
   |                      ^ move out of `c` occurs here

How can I guarantee to the compiler that the reference to c taken in the closure will still be valid? I assume it will be, since execution of the closure is awaited before c is returned.

CodePudding user response:

You could move the camera into the inner closure, then return it once the frame capture is complete:

    stream::unfold(camera, |c| async move {
        Some(blocking::unblock(|| move {
            let frame = c.capture().unwrap()).await;
            (frame,c)
        })
    })

CodePudding user response:

.await does not guarantee liveness. This is the general problem of scoped async tasks. Futures can be canceled at any time. Consider:

let future = async {
    let local = 123;
    blocking::unblock(|| local).await;
};

// Poll `future`, but only once.
futures::block_on(async move { futures::poll!(future) });

We started a task using local data, then dropped the future. The task continues executing but the local data is gone. For this reason, there is currently no sound way to expose an async API allowing using local data similar to scoped threads. You have to use 'static data, for example by wrapping in Arc.

See also blocking issue #4.

  • Related