I'm a new rustacean and I try to pass a function as argument in another function in order to create threads with the function pass as argument.
Here the code:
use std::os::unix::net::{UnixListener, UnixStream};
use std::thread;
use std::io::Read;
use anyhow::Context;
pub struct SocketServer {
path: String,
listener: UnixListener,
}
impl SocketServer {
pub fn new(path: &str) -> anyhow::Result<SocketServer>{
if std::fs::metadata(path).is_ok() {
println!("A socket is already present. Deleting...");
std::fs::remove_file(path).with_context(|| {
format!("could not delete previous socket at {:?}", path)
})?;
}
let socket_listener =
UnixListener::bind(path).context("Could not create the unix socket")?;
let path = path.to_string();
Ok(SocketServer{ path, listener: socket_listener })
}
pub fn start(&self, f: &dyn Fn(UnixStream)) -> anyhow::Result<()>{
for stream in self.listener.incoming() {
match stream {
Ok(stream) => {
thread::spawn(||f(stream));
}
Err(err) => {break;}
}
}
Ok(())
}
}
pub fn handle_stream(mut unix_stream: UnixStream) -> anyhow::Result<()> {
let mut message = String::new();
unix_stream
.read_to_string(&mut message)
.context("Failed at reading the unix stream")?;
println!("We received this message: {}", message);
Ok(())
}
Here the error I get when in try to compile:
error[E0277]: `dyn Fn(UnixStream)` cannot be shared between threads safely
--> src/socket.rs:34:35
|
34 | thread::spawn(||f(stream));
| ------------- ^^^^^^^^^^^ `dyn Fn(UnixStream)` cannot be shared between threads safely
| |
| required by a bound introduced by this call
|
= help: the trait `Sync` is not implemented for `dyn Fn(UnixStream)`
= note: required for `&dyn Fn(UnixStream)` to implement `Send`
I got some information in the Rust book but I still don't understand which function need to implement what.
Can you give me some hints? (Advice on other parts are welcome too)
I tried to remove closure but it goes to another error:
error[E0277]: expected a `FnOnce<()>` closure, found `()`
--> src/socket.rs:34:35
|
34 | thread::spawn(f(stream));
| ------------- ^^^^^^^^^ expected an `FnOnce<()>` closure, found `()`
| |
| required by a bound introduced by this call
|
= help: the trait `FnOnce<()>` is not implemented for `()`
= note: wrap the `()` in a closure with no arguments: `|| { /* code */ }`
note: required by a bound in `spawn`
--> /rust/lib/rustlib/src/rust/library/std/src/thread/mod.rs:661:8
|
661 | F: FnOnce() -> T,
| ^^^^^^^^^^^^^ required by this bound in `spawn`
Is there a reason you need a dyn Fn?
The easiest is probably to accept a impl Fn(UnixStream) + Send + Clone + 'static instead
pub fn start<F>(&self, f: F) -> anyhow::Result<()>
where
F: Fn(UnixStream) + Send + Clone + 'static,
{
for stream in self.listener.incoming() {
let f = f.clone();
match stream {
Ok(stream) => {
thread::spawn(move || f(stream));
}
Err(err) => break,
}
}
Ok(())
}
The dyn Fn() may capture things (such as Rcs) that are unsafe to share between threads. So you need to mark it Sync:
pub fn start(&self, f: &(dyn Fn(UnixStream) + Sync)) -> anyhow::Result<()> {
Now you'll get another error:
error[E0521]: borrowed data escapes outside of associated function
--> src/lib.rs:34:21
|
30 | pub fn start(&self, f: &(dyn Fn(UnixStream) + Sync)) -> anyhow::Result<()> {
| - - let's call the lifetime of this reference `'1`
| |
| `f` is a reference that is only valid in the associated function body
...
34 | thread::spawn(|| f(stream));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| `f` escapes the associated function body here
| argument requires that `'1` must outlive `'static`
Because the function's captured data may be dropped before the thread exits, causing use after free. The easiest solution is to use Arc:
pub fn start(&self, f: Arc<dyn Fn(UnixStream) + Sync + Send>) -> anyhow::Result<()> {
for stream in self.listener.incoming() {
match stream {
Ok(stream) => {
let f = Arc::clone(&f);
thread::spawn(move || f(stream));
}
Err(err) => {
break;
}
}
}
Ok(())
}
(You also need a Send bound).
Related
I do a server update in rust. it create patches between 2 binaries files, and serves static files
I try to do
let mut update_state;
if let Some(state) = update_stream.next().await {
if let Ok(state) = state {
update_state = state
} else if let Err(err) = state {
reply = BuildOutput { error: "Update failed: ".to_string() + &err.to_string() }
}
} else {
reply = BuildOutput { error: "Unreacheable".to_string() }
}
let state = update_state.borrow();
let progress = state.histogram.progress();
let res = update_stream.try_for_each(|_state| future::ready(Ok(()))).await;
but get
note: future is not `Send` as this value is used across an await
--> server\grpc\src\rpc.rs:260:50
|
259 | let mut update_state;
| ---------------- has type `SharedUpdateProgress` which is not `Send`
260 | if let Some(state) = update_stream.next().await {
| ^^^^^^ await occurs here, with `mut update_state` maybe used later
...
305 | }
| - `mut update_state` is later dropped here
= note: required for the cast from `impl futures::Future<Output = Result<tonic::Response<BuildOutput>, Status>>` to the object type `dyn futures::Future<Output = Result<tonic::Response<BuildOutput>, Status>> + std::marker::Send
SharedUpdateProgress:
#[derive(Clone)]
pub struct SharedUpdateProgress {
state: Rc<RefCell<UpdateProgress>>,
}
impl SharedUpdateProgress {
pub fn new(target_revision: CleanName) -> Self {
Self { state: Rc::new(RefCell::new(UpdateProgress::new(target_revision))) }
}
pub fn borrow(&self) -> Ref<'_, UpdateProgress> {
self.state.borrow()
}
pub(crate) fn borrow_mut(&self) -> RefMut<'_, UpdateProgress> {
self.state.borrow_mut()
}
}
I don't know why and don't know how to fix it
I assume a minimal reproducible example of your problem is as follows:
use std::{cell::RefCell, rc::Rc};
use tokio::time::{sleep, Duration};
#[derive(Clone)]
pub struct SharedString {
state: Rc<RefCell<String>>,
}
impl SharedString {
pub fn new(initial: &str) -> Self {
Self {
state: Rc::new(RefCell::new(initial.into())),
}
}
}
async fn run() {
let shared_string = SharedString::new("Hello,");
sleep(Duration::from_millis(1)).await;
*shared_string.state.borrow_mut() += " world!";
sleep(Duration::from_millis(1)).await;
println!("{:?}", shared_string.state.borrow());
}
#[tokio::main]
async fn main() {
tokio::task::spawn(run()).await.unwrap();
}
error: future cannot be sent between threads safely
--> src/main.rs:27:24
|
27 | tokio::task::spawn(run()).await.unwrap();
| ^^^^^ future returned by `run` is not `Send`
|
= help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<RefCell<String>>`
note: future is not `Send` as this value is used across an await
--> src/main.rs:19:36
|
18 | let shared_string = SharedString::new("Hello,");
| ------------- has type `SharedString` which is not `Send`
19 | sleep(Duration::from_millis(1)).await;
| ^^^^^^ await occurs here, with `shared_string` maybe used later
...
23 | }
| - `shared_string` is later dropped here
note: required by a bound in `tokio::spawn`
--> /home/martin/.cargo/git/checkouts/tokio-dd4afa005f1f4b79/686577b/tokio/src/task/spawn.rs:163:21
|
163 | T: Future + Send + 'static,
| ^^^^ required by this bound in `tokio::spawn`
The tokio Runtime is usually multi-threaded, meaning that at any .await point your task could get moved from one thread to another. That's why everything that is held across an .await point must be Send. Which Rc<RefCell<>> is explicitely not, because it's a single-threaded reference counter.
Solution: Replace Rc<RefCell<>> with Arc<Mutex<>>, which is the thread-safe equivalent.
use std::sync::{Arc, Mutex};
use tokio::time::{sleep, Duration};
#[derive(Clone)]
pub struct SharedString {
state: Arc<Mutex<String>>,
}
impl SharedString {
pub fn new(initial: &str) -> Self {
Self {
state: Arc::new(Mutex::new(initial.into())),
}
}
}
async fn run() {
let shared_string = SharedString::new("Hello,");
sleep(Duration::from_millis(1)).await;
*shared_string.state.lock().unwrap() += " world!";
sleep(Duration::from_millis(1)).await;
println!("{:?}", shared_string.state.lock().unwrap());
}
#[tokio::main]
async fn main() {
tokio::task::spawn(run()).await.unwrap();
}
"Hello, world!"
In Yew, I am attempting to bind a callback to the window resize event, triggering a Msg::Resize update. I have encountered: E0631 Type mismatch in closure arguments.`
Expanded version of the code can be found here:
https://github.com/JamesMcGuigan/fractals/blob/bd1fb20e138696fc2af9b121a13292528336b622/src/components/canvas_question.rs
This is the minimalist test case:
use gloo_console::log;
use gloo_events::EventListener;
use yew::prelude::*;
pub struct CanvasQuestion {}
pub enum Msg { Resize }
impl Component for CanvasQuestion {
type Message = Msg;
type Properties = ();
fn create(_ctx: &Context<Self>) -> Self {
Self { }
}
fn view(&self, _ctx: &Context<Self>) -> Html {
html! { <canvas id="canvas"/> }
}
fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
if first_render {
// WORKS
ctx.link().send_message(Msg::Resize);
// found signature of `fn(yew::Event)
// let on_window_resize = |_event: Event| { // BROKEN
let on_window_resize = |_event: &Event| { // WORKS
ctx.link().send_message(Msg::Resize);
};
// expected signature of `for<'r> fn(&'r yew::Event)
EventListener::new( &web_sys::window().unwrap(),
"resize", on_window_resize );
}
}
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::Resize => { true } // rerender
}
}
}
Update
Replacing on_window_resize = |_event: Event| with on_window_resize = |_event: &Event| fixes the below errors
error[E0631]: type mismatch in closure arguments
--> src/components/canvas_question.rs:68:43
|
64 | let on_window_resize = |_event: Event| {
| --------------- found signature of `fn(yew::Event) -> _`
...
67 | EventListener::new( &web_sys::window().unwrap(),
| ------------------ required by a bound introduced by this call
68 | "resize", on_window_resize );
| ^^^^^^^^^^^^^^^^ expected signature of `for<'r> fn(&'r yew::Event) -> _`
|
note: required by a bound in `EventListener::new`
--> /home/jamie/.cargo/registry/src/github.com-1ecc6299db9ec823/gloo-events-0.1.2/src/lib.rs:338:12
|
338 | F: FnMut(&Event) + 'static,
| ^^^^^^^^^^^^^ required by this bound in `EventListener::new`
But exposes rust lifetime issues trying to access ctx.link() from inside the closure.
error[E0521]: borrowed data escapes outside of associated function
--> src/components/fractal.rs:70:28
|
51 | fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
| --- - let's call the lifetime of this reference `'1`
| |
| `ctx` is a reference that is only valid in the associated function body
...
70 | let listener = EventListener::new( &web_sys::window().unwrap(),
| ____________________________^
71 | | "resize", on_window_resize );
| | ^
| | |
| |___________________________________________________________________________`ctx` escapes the associated function body here
| argument requires that `'1` must outlive `'static`
Is there a way of defining a 'static lifetime for ctx: Context?
Is ctx.link() the best way to trigger internal message passing?
Would it be easier to call self.update(Msg::Resize) directly instead?
Does ctx.link() need to be stored as self.link in the CanvasQuestion struct?
What is the correct way to write and bind the on_window_resize() function?
#Dogbert's comment of passing a reference to |_event: &Event| fixed error[E0631]: type mismatch in closure arguments, butr this turned out not to be needed in the final solution.
RTFM discovered the manual had the code pattern I needed
https://yew.rs/docs/concepts/html/events
This uses the move keyword to pass ctx.link().callback() into the closure by value rather than reference, thus avoiding the lifetimes issue.
pub struct CanvasQuestion {
listener: Option<EventListener>,
}
impl Component for CanvasQuestion {
fn create(_ctx: &Context<Self>) -> Self {
Self {
listener: None,
}
}
fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
if first_render {
ctx.link().send_message(Msg::Resize);
let onresize = ctx.link().callback(|_: Event| Msg::Resize);
let listener = EventListener::new(
&web_sys::window().unwrap(),
"resize",
move |e| onresize.emit(e.clone())
);
self.listener = Some(listener);
}
}
}
https://github.com/JamesMcGuigan/fractals/blob/a7dce50ecbcb71bb9c4c6f8e5194026fed59cc66/src/components/canvas_question.rs
I'm trying to move some data around b/w different threads but am getting the ole Copy trait-not-implemented error. Here's some code:
use std::future::Future;
use std::marker::PhantomData;
use std::sync::{Arc, Mutex};
/// Start external crates mocked here
#[derive(Clone, PartialEq, Eq)]
pub struct DecodeError {
inner: Box<Inner>,
}
#[derive(Clone, PartialEq, Eq)]
struct Inner {}
#[derive(Clone)]
pub struct Connection {}
pub trait Message: core::fmt::Debug + Send + Sync {
fn decode<B>(mut buf: B) -> Result<Self, DecodeError>
where
B: bytes::Buf,
Self: Default,
{
// do stuff
let mut message = Self::default();
Ok(message)
}
}
#[derive(Clone, Debug, Default)]
pub struct Request {}
impl Message for Request {}
#[derive(Clone, Debug, Default)]
pub struct Response {}
impl Message for Response {}
pub struct OtherResponse {}
pub enum ReplyError {
InvalidData,
}
pub struct EventMessage {
data: Vec<u8>,
}
pub struct Subscription {}
impl Subscription {
pub async fn next(&self) -> Option<EventMessage> {
Some(EventMessage { data: vec![] })
}
}
/// End external crates mocked here
#[derive(Clone)]
pub struct Publisher<T> {
connection: Connection,
subject: String,
resource_type: PhantomData<*const T>,
}
#[derive(Debug)]
pub enum PublishError {
SerializeError(String),
PublishError(String),
}
pub type PublishResult<T> = std::result::Result<T, PublishError>;
impl<T: Message> Publisher<T> {
pub fn new(connection: Connection, subject: String) -> Self {
let resource_type = PhantomData;
Publisher {
connection: connection,
subject,
resource_type,
}
}
pub async fn publish(&self, msg: T) -> PublishResult<()>
where
T: Message,
{
// do stuff to msg
Ok(())
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let node = Node::new("127.0.0.1", "node".into())
.await
.expect("connecting to NATS");
let p: Publisher<Request> = node.get_publisher("TOPIC".into());
let _submission_replyer: AsynkReplyer<Request, Response> = node
.get_replyer("request".into(), move |req: Arc<Mutex<Request>>| async {
let mut req = req.clone().lock().unwrap();
p.clone().publish(*req);
Ok(Response {})
})
.await;
Ok(())
}
pub struct Node {
name: String,
connection: Connection,
}
pub type ReplyResult<T> = std::result::Result<T, ReplyError>;
impl Node {
pub async fn new(_nats_url: &str, name: String) -> std::io::Result<Self> {
env_logger::init();
let connection = Connection {};
Ok(Node { name, connection })
}
pub fn get_publisher<T>(&self, subject: String) -> Publisher<T>
where
T: Message + Default,
{
Publisher::new(self.connection.clone(), subject)
}
pub async fn get_replyer<Req, Resp, Fut>(
&self,
subject: String,
callback: impl Fn(Arc<Mutex<Req>>) -> Fut + Send + Sync + 'static + Copy,
) -> AsynkReplyer<Req, Resp>
where
Req: Message + Default + 'static,
Resp: Message + Default,
Fut: Future<Output = ReplyResult<Resp>> + Send,
{
AsynkReplyer::new(&self.connection, subject, callback).await
}
}
pub struct AsynkReplyer<Req, Resp> {
request_type: PhantomData<Req>,
response_type: PhantomData<Resp>,
}
impl<Req: Message + Default + 'static, Resp: Message + Default> AsynkReplyer<Req, Resp> {
pub async fn new<Fut>(
connection: &Connection,
subject: String,
callback: impl Fn(Arc<Mutex<Req>>) -> Fut + Send + Sync + 'static + Copy,
) -> AsynkReplyer<Req, Resp>
where
Fut: Future<Output = ReplyResult<Resp>> + Send,
{
Self::start_subscription_handler(Subscription {}, callback).await;
AsynkReplyer {
request_type: PhantomData,
response_type: PhantomData,
}
}
pub async fn start_subscription_handler<Fut>(
subscription: Subscription,
callback: impl Fn(Arc<Mutex<Req>>) -> Fut + Send + Sync + 'static + Copy,
) where
Fut: Future<Output = ReplyResult<Resp>> + Send,
{
tokio::spawn(async move {
loop {
match subscription.next().await {
Some(msg) => {
Self::handle_request(msg, callback).await;
}
None => {
break;
}
}
}
});
}
/// Decodes + spins up another task to handle the request
pub async fn handle_request<Fut>(
msg: EventMessage,
callback: impl Fn(Arc<Mutex<Req>>) -> Fut + Send + Sync + 'static + Copy,
) -> ReplyResult<()>
where
Fut: Future<Output = ReplyResult<Resp>> + Send,
{
let decoded = Req::decode(msg.data.as_slice()).map_err(|_| ReplyError::InvalidData)?;
tokio::spawn(async move {
match callback(Arc::new(Mutex::new(decoded))).await {
Ok(response) => {
// do stuff
}
Err(e) => {}
}
});
Ok(())
}
}
error:
error[E0277]: the trait bound `Publisher<Request>: std::marker::Copy` is not satisfied in `[closure#src/main.rs:93:40: 97:10]`
--> src/main.rs:93:10
|
93 | .get_replyer("request".into(), move |req: Arc<Mutex<Request>>| async {
| __________^^^^^^^^^^^___________________-
| | |
| | within `[closure#src/main.rs:93:40: 97:10]`, the trait `std::marker::Copy` is not implemented for `Publisher<Request>`
94 | | let mut req = req.clone().lock().unwrap();
95 | | p.clone().publish(*req);
96 | | Ok(Response {})
97 | | })
| |_________- within this `[closure#src/main.rs:93:40: 97:10]`
|
= note: required because it appears within the type `[closure#src/main.rs:93:40: 97:10]`
error[E0277]: `*const Request` cannot be sent between threads safely
--> src/main.rs:93:10
|
93 | .get_replyer("request".into(), move |req: Arc<Mutex<Request>>| async {
| __________^^^^^^^^^^^___________________-
| | |
| | `*const Request` cannot be sent between threads safely
94 | | let mut req = req.clone().lock().unwrap();
95 | | p.clone().publish(*req);
96 | | Ok(Response {})
97 | | })
| |_________- within this `[closure#src/main.rs:93:40: 97:10]`
|
= help: within `[closure#src/main.rs:93:40: 97:10]`, the trait `Send` is not implemented for `*const Request`
= note: required because it appears within the type `PhantomData<*const Request>`
note: required because it appears within the type `Publisher<Request>`
--> src/main.rs:53:12
|
53 | pub struct Publisher<T> {
| ^^^^^^^^^
= note: required because it appears within the type `[closure#src/main.rs:93:40: 97:10]`
error[E0277]: `*const Request` cannot be shared between threads safely
--> src/main.rs:93:10
|
93 | .get_replyer("request".into(), move |req: Arc<Mutex<Request>>| async {
| __________^^^^^^^^^^^___________________-
| | |
| | `*const Request` cannot be shared between threads safely
94 | | let mut req = req.clone().lock().unwrap();
95 | | p.clone().publish(*req);
96 | | Ok(Response {})
97 | | })
| |_________- within this `[closure#src/main.rs:93:40: 97:10]`
|
= help: within `[closure#src/main.rs:93:40: 97:10]`, the trait `Sync` is not implemented for `*const Request`
= note: required because it appears within the type `PhantomData<*const Request>`
note: required because it appears within the type `Publisher<Request>`
--> src/main.rs:53:12
|
53 | pub struct Publisher<T> {
| ^^^^^^^^^
= note: required because it appears within the type `[closure#src/main.rs:93:40: 97:10]`
I can't (or can I) add the Copy attribute on the Publisher struct but that wont work since not all of its fields implement Copy. Despite this I've commented out the fields in Publisher that don't impl Copy and added the attribute to it just to see, and with that approach I get:
the trait `std::marker::Copy` is not implemented for `Request`
Request is a protobuf based struct compiled using the prost lib. I'm not able to add the Copy attribute to that because of some of its fields not implementing Copy such as String and Timestamp.
I'm wondering if the design here is just inherently bad or if there's a simple fix.
It seems to me, you've constrained that the Fn is Copy because you are passing it to multiple tokio::spawn calls. You've found that Copy is very restrictive, however Clone is not. You should use it instead and simply call .clone() when you handle the new request:
Self::handle_request(msg, callback.clone()).await;
Then the only errors are '*const Request' cannot be sent between threads safely. The compiler does not automatically implement Send or Sync for pointers because it doesn't know if that's safe, but your Fn needs to be called from different threads. Fortunately, you don't need to worry about that. Whether your PhantomData<*const T> is there simply to satisfy the compiler or to enforce specific variance, you can get the same result like this:
resource_type: PhantomData<fn() -> *const T>
Then, now that we've fixed the type constraint errors, the compiler now produces errors about lifetimes:
req.clone().lock().unwrap() doesn't work because the result of .lock() is tied to the value from req.clone(), but that gets dropped immediately. The fix is that the .clone() is unnecessary and can be removed.
p.clone().publish(*req) doesn't work since dereferencing a MutexLockGuard cannot provide a owned value, only a reference. You can fix this by adding a .clone() instead. If instead you think the Arc parameter is exclusive, you can get ownership by following the advice here: How to take ownership of T from Arc<Mutex<T>>?
The last lifetime error is a bit fuzzy because it has to do with the lifetime of the returned Future being tied to the req parameter. This can be fixed by using async move { } but then p is moved out of the closure into the future, meaning it is no longer Fn. What you want is to move req but move a clone of p. You can do this like so:
move |req: Arc<Mutex<Request>>| {
let p = p.clone();
async move {
// ...
}
}
"now I'm trying to resolve errs related to await-ing p.publish" - the error has to do with the lock now persisting across an await point, but since the mutex guard doesn't implement Send, the Future cannot be Send. You can fix this by locking and cloning in one step, so the lock isn't held:
let req = req.lock().unwrap().clone();
p.publish(req);
Ok(Response {})
See this compiling on the playground. There are still a number of warnings that should be addressed (unused Results), but I hope this gets you on the right path.
It is really very difficult to help you with this information. The full error code would probably be useful.
Anyway, in "impl Node ... get_replyer()" you see that's callback should return somehink that's implement Copy
pub async fn get_replyer<Req, Resp, Fut>(
&self,
subject: String,
callback: impl Fn(Arc<Mutex<Req>>) -> Fut + Send + Sync + 'static + Copy,
//-------------------------------------------------------------------^
) -> AsynkReplyer<Req, Resp>
In main
let _submission_replyer: AsynkReplyer<Resuest, Response> = node
.get_replyer(
"request".into(),
move |req: Arc<Mutex<Request>>| async {
let mut req = req.lock().unwrap();
p.publish(*req);
Ok(Response {
header: None,
response: Some(OtherResponse {
request: None,
status: 0,
}),
})
//-----------^------------------------------- Return a enum std::result::Result
},
)
.await;
std::result::Result implement Copy but wath about Response? The error is showed at this point?
I'm trying to store multiple async callbacks in a single hash map which I later iterate over. This is my closest attempt to getting it working however, I'm getting a compilation error because the lifetime of the callback.
use std::boxed::Box;
use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
type AsyncCallback = Box<dyn Fn() -> Pin<Box<dyn Future<Output = ()>>>>;
#[derive(Default)]
pub struct CallbackMap {
callbacks: HashMap<&'static str, AsyncCallback>,
}
impl CallbackMap {
pub fn add<C, F>(&mut self, name: &'static str, callback: C)
where
C: Fn() -> F,
C: 'static,
F: Future<Output = ()> + 'static,
{
self.callbacks
.insert(name, Box::new(|| Box::pin(callback())));
}
pub async fn execute(&self) {
for (_key, value) in &self.callbacks {
value().await;
}
}
}
async fn async_callback() {
println!("Callback 2");
}
fn main() {
let mut callbacks = CallbackMap::default();
callbacks.add("test1", || async {
println!("Callback 1");
});
callbacks.add("test2", async_callback);
callbacks.execute();
}
Here is the error I'm getting:
error[E0597]: `callback` does not live long enough
--> src/main.rs:21:48
|
21 | .insert(name, Box::new(|| Box::pin(callback())));
| ---------------------^^^^^^^^----
| | | |
| | | borrowed value does not live long enough
| | value captured here
| cast requires that `callback` is borrowed for `'static`
22 | }
| - `callback` dropped here while still borrowed
Use the move keyword to move callback into the closure:
self.callbacks
.insert(name, Box::new(move || Box::pin(callback())));
// ^^^^
Look at this code:
#![feature(async_closure)]
use std::future::Future;
use std::pin::Pin;
trait A<'a> {
fn call(&'a self, data: &'a i32) -> Pin<Box<dyn 'a + Future<Output=()>>>;
}
impl <'a, F, Fut> A<'a> for F
where Fut: 'a + Future<Output=()>,
F: Fn(&'a i32) -> Fut
{
fn call(&'a self, data: &'a i32) -> Pin<Box<dyn 'a + Future<Output=()>>> {
Box::pin(self(data))
}
}
async fn sample(_data: &i32) {
}
fn is_a(_: impl for<'a> A<'a>) {
}
fn main() {
is_a(sample);
is_a(async move |data: &i32| {
println!("data: {}", data);
});
}
Playground
Why does is_a(sample) works but the next line fails to be compiled? What's the difference of lifetime inference between async fn and async closure?
The closure version fails with the following error:
error: implementation of `A` is not general enough
--> src/main.rs:29:5
|
6 | / trait A<'a> {
7 | | fn call(&'a self, data: &'a i32) -> Pin<Box<dyn 'a + Future<Output=()>>>;
8 | | }
| |_- trait `A` defined here
...
29 | is_a(async move |data: &i32| {
| ^^^^ implementation of `A` is not general enough
|
= note: `A<'1>` would have to be implemented for the type `[closure#src/main.rs:29:10: 31:6]`, for any lifetime `'1`...
= note: ...but `A<'_>` is actually implemented for the type `[closure#src/main.rs:29:10: 31:6]`, for some specific lifetime `'2`
The return type of an async || closure is an anonymous type generated by the compiler.
Such type implements a Future and it captures an additional
lifetime related to the scope of the async || closure.
fn main() {
let closure = async move |data: &i32| { --+ '2 start
println!("data: {}", data); |
}; |
|
is_a(closure); |
v
}
The async block returns a type with signature like:
impl Future<Output = SomeType> + '2 + '...
Where '2 is the lifetime of the closure.
Note that there isn't this additional lifetime requirement when using an async function instead of a closure.
When you call is_a like this:
let closure = async move |data: &i32| {
println!("data: {}", data);
};
is_a(closure);
You get:
error: implementation of `A` is not general enough
...
= note: `A<'1>` would have to be implemented for the type `[closure#src/main.rs:43:19: 45:6]`, for any lifetime `'1`...
= note: ...but `A<'_>` is actually implemented for the type `[closure#src/main.rs:43:19: 45:6]`, for some specific lifetime `'2`
because the closure argument is a type implemented for a specific lifetime '2 but any lifetime is required:
fn is_a(_: impl for<'a> A<'a>) {}
Note that the error you notice indeed hides another lifetime violation that originate from the captured '2 lifetime.
For:
let closure = async move |data: &i32| {
println!("data: {}", data);
};
the compiler reports:
error: lifetime may not live long enough
--> src/main.rs:43:19
|
43 | let closure = async move |data: &i32| {
| ^^^^^^^^^^^^^^^^^^-^^^-
| | | |
| | | return type of closure is impl std::future::Future
| | let's call the lifetime of this reference `'1`
| returning this value requires that `'1` must outlive `'2`
that let me conclude that it is not possible to use argument references inside an async || closure.
Why is it needed lifetime '2?
The lifetime concept is about memory safety and it boils down to garantee that a reference
pointing to a memory slot is pointing to a valid value.
fn my_function() {
let value = 1 --+ '1 start
|
let closure = async move |data: &i32| { | --+ '2 start
println!("data: {}", data); | |
}; | |
| |
tokio::spawn(closure(&value)) | |
-+ '1 end |
} v continue until
the future complete
Consider the above example: value is a memory slot allocated on the stack and it will be valid until
my_function returns and the stack unwind.
The lifetime '1 take account of the scope of validity of value, when my_function returns
the reference &value is not more valid.
But from where is coming lifetime '2?
It is because closure(&value) returns an entity that implements a Future that will live into the runtime executor,
in this case the tokio executor, until the computation will end.
The '2 lifetime will take this scope of validity of the Future into account.
For making a reason of '2 lifetime necessity consider the following scenario:
fn run_asyn_closure() {
let data: i32 = 1;
let closure = async move |data: &i32| {
println!("starting task with data {}", data);
// yield the computation for 3 seconds, awaiting for completion of long_running_task
long_running_task().await;
// data points to a memory slot on the stack that meantime is rewritten
// because run_asyn_closure returned 3 seconds ago
println!("using again data: {}", data); // BANG!! data is not more valid
};
tokio::spawn(closure(&data));
}
Note that in reality tokio::spawn needs that &data reference has 'static lifetime,
but this is irrelevant to understand this theme.