简体   繁体   中英

Rust lifetimes in async wrapper for sync code

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.

Solution

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

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)
        })
    })

.await does not guarantee liveness. This is the general problem ofscoped 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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM