Store async callback in map - rust

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())));
// ^^^^

Related

Why it is so difficult to pass this &mut db transaction to this closure?

I'm using the below code in my real project. (Obviously this is simplified and one file only for playground.)
Rust explorer playground
As you can see I'm trying to use a sqlx Transaction from one function to a closure.
But I'm stucked.
I don't even know if this (so common Golang pattern) is the best wat to do in Rust. But at least it should work now.
use std::{future::Future, pin::Pin, sync::Arc};
pub trait Trait: Send + Sync + Player + Shirt {}
impl<T: Player + Shirt> Trait for T {}
pub type Lambda<'a, ArgT, ResT> =
dyn Fn(ArgT) -> Pin<Box<dyn Future<Output = Result<ResT, String>> + Send + 'a>> + Sync + 'a;
#[async_trait::async_trait]
pub trait Player: Send + Sync {
async fn player_create<'a>(
&'a self,
_input: &PlayerInput,
lambda: &Lambda<'_, PlayerCreateLambdaArgs<'a>, DomainPlayer>,
) -> Result<DomainPlayer, String>;
}
#[async_trait::async_trait]
pub trait Shirt: Send + Sync {
async fn shirt_get_next_and_increase<'a>(
&'a self,
tx: &'a mut sqlx::PgConnection,
model: String,
) -> Result<i64, String>;
}
pub struct Repo {
pub pool: Arc<sqlx::PgPool>,
}
impl Repo {
pub fn new(pool: Arc<sqlx::PgPool>) -> Self {
Self { pool }
}
}
#[async_trait::async_trait]
impl Player for Repo {
async fn player_create<'a>(
&'a self,
_input: &PlayerInput,
lambda: &Lambda<'_, PlayerCreateLambdaArgs<'a>, DomainPlayer>,
) -> Result<DomainPlayer, String> {
let mut tx = self.pool.begin().await.unwrap();
// use _input here
let shirt_next_value = Box::new(|model: String| {
self::Shirt::shirt_get_next_and_increase(self, &mut tx, model)
});
let domain_player = lambda(PlayerCreateLambdaArgs { shirt_next_value }).await?;
let res =
sqlx::query_as::<_, DomainPlayer>("INSERT INTO player (...) VALUES (...) RETURNING *")
.bind(domain_player.id)
.bind(domain_player.shirt_number)
.fetch_one(&mut *tx)
.await
.unwrap();
Ok(res)
}
}
#[async_trait::async_trait]
impl Shirt for Repo {
async fn shirt_get_next_and_increase<'a>(
&'a self,
_tx: &'a mut sqlx::PgConnection,
_model: String,
) -> Result<i64, String> {
// Here I'm awaiting an async call for DB operations using the same DB transacion of the caller (_tx)...
// use _tx here...
let res = 123;
Ok(res)
}
}
pub struct Needs {
pub command_pg_repo: Arc<dyn Trait>,
}
#[derive(Default)]
pub struct PlayerInput {
pub id: String,
}
#[derive(Debug, Default, Clone, sqlx::FromRow)]
pub struct DomainPlayer {
pub id: String,
pub shirt_number: i64,
}
pub struct PlayerCreateLambdaArgs<'a> {
// other needed fields here
pub shirt_next_value: Box<
dyn FnMut(String) -> Pin<Box<dyn Future<Output = Result<i64, String>> + Send + 'a>>
+ Send
+ Sync
+ 'a,
>,
}
pub struct Handler {
needs: Arc<Needs>,
}
impl Handler {
pub fn new(needs: Arc<Needs>) -> Self {
Self { needs }
}
pub async fn handle(&self, input: &PlayerInput) -> Result<DomainPlayer, String> {
let res = self
.needs
.command_pg_repo
.player_create(&input, &|mut args| {
let input = input;
Box::pin(async move {
let shirt_number = (args.shirt_next_value)("player".to_string()).await?;
let o = DomainPlayer {
id: input.id.to_string(),
shirt_number,
};
Ok(o)
})
})
.await?;
Ok(res)
}
}
#[tokio::main]
async fn main() -> Result<(), String> {
let db_conn = sqlx::PgPool::connect("fake_url").await.unwrap();
let pg_repo = Arc::new(Repo::new(Arc::new(db_conn)));
let needs = Arc::new(Needs {
command_pg_repo: pg_repo,
});
let handler = Handler::new(needs);
let new_player_input = PlayerInput {
id: "abc".to_string(),
};
let player = handler.handle(&new_player_input).await?;
dbg!(player);
Ok(())
}
The error:
error: captured variable cannot escape `FnMut` closure body
--> src/main.rs:62:13
|
57 | let mut tx = self.pool.begin().await.unwrap();
| ------ variable defined here
...
61 | let shirt_next_value = Box::new(|model: String| {
| - inferred to be a `FnMut` closure
62 | self::Shirt::shirt_get_next_and_increase(self, &mut tx, model)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--^^^^^^^^
| | |
| | variable captured here
| returns a reference to a captured variable which escapes the closure body
|
= note: `FnMut` closures only have access to their captured variables while they are executing...
= note: ...therefore, they cannot allow references to captured variables to escape
error[E0597]: `tx` does not live long enough
--> src/main.rs:62:65
|
55 | lambda: &Lambda<'_, PlayerCreateLambdaArgs<'a>, DomainPlayer>,
| ------ lifetime `'1` appears in the type of `lambda`
...
61 | let shirt_next_value = Box::new(|model: String| {
| --------------- value captured here
62 | self::Shirt::shirt_get_next_and_increase(self, &mut tx, model)
| ^^ borrowed value does not live long enough
...
65 | let domain_player = lambda(PlayerCreateLambdaArgs { shirt_next_value }).await?;
| ---------------- this usage requires that `tx` is borrowed for `'1`
...
76 | }
| - `tx` dropped here while still borrowed
error[E0499]: cannot borrow `tx` as mutable more than once at a time
--> src/main.rs:71:34
|
55 | lambda: &Lambda<'_, PlayerCreateLambdaArgs<'a>, DomainPlayer>,
| ------ lifetime `'1` appears in the type of `lambda`
...
61 | let shirt_next_value = Box::new(|model: String| {
| --------------- first mutable borrow occurs here
62 | self::Shirt::shirt_get_next_and_increase(self, &mut tx, model)
| -- first borrow occurs due to use of `tx` in closure
...
65 | let domain_player = lambda(PlayerCreateLambdaArgs { shirt_next_value }).await?;
| ---------------- this usage requires that `tx` is borrowed for `'1`
...
71 | .fetch_one(&mut *tx)
| ^^ second mutable borrow occurs here
Some errors have detailed explanations: E0499, E0597.
For more information about an error, try `rustc --explain E0499`.
There are 3 main issues with the code (more like 2 and a half):
Closures cannot return references to things the closure owns, or things the closure borrowed mutably (More formally: the return type of the Fn* function/closure family of traits cannot borrow from the closure type itself). One way to fix this is to have tx be moved into the closure, and then moved into the closure's returned Future. Shared ownership can be achieved with std::sync::Arc, and cloning it and moving it when you need to give another task ownership.
Fn closures may be called concurrently, so they cannot use mutable references to things the closure owns or has borrowed, but this code tries to use a &mut tx borrowing from the outer scope inside a Fn closure. (This issue is bypassed with the solution outlined below).
If you want to access something mutably when it is shared, you need to synchronize those accesses so only one thing can actually access it at a time. One way to do this in async Rust is using tokio::sync::Mutex.
Combining the above, putting tx into a Arc<Mutex<...>>, moving .clone()s of the Arc into the tasks that need ownership, and .lock().awaiting whenever you need to access it mutably may fix your problem (it makes it compile at least).
Rust explorer
diff --git a/src/main.rs b/src/main.rs
index 89fc611..6f7d375 100644
--- a/src/main.rs
+++ b/src/main.rs
## -1,5 +1,7 ##
use std::{future::Future, pin::Pin, sync::Arc};
+use tokio::sync::Mutex;
+
pub trait Trait: Send + Sync + Player + Shirt {}
impl<T: Player + Shirt> Trait for T {}
## -42,12 +44,19 ## impl Player for Repo {
_input: &PlayerInput,
lambda: &Lambda<'_, PlayerCreateLambdaArgs<'a>, DomainPlayer>,
) -> Result<DomainPlayer, String> {
- let mut tx = self.pool.begin().await.unwrap();
+ let tx = Arc::new(Mutex::new(self.pool.begin().await.unwrap()));
// use _input here
- let shirt_next_value = Box::new(|model: String| {
- self::Shirt::shirt_get_next_and_increase(self, &mut tx, model)
+ let shirt_next_value = Box::new({
+ let tx = tx.clone();
+ move |model: String| -> Pin<Box<dyn Future<Output = Result<i64, std::string::String>> + Send>> {
+ let tx = tx.clone();
+ Box::pin(async move {
+ self::Shirt::shirt_get_next_and_increase(self, &mut *tx.lock().await, model)
+ .await
+ })
+ }
});
let domain_player = lambda(PlayerCreateLambdaArgs { shirt_next_value }).await?;
## -56,7 +65,7 ## impl Player for Repo {
sqlx::query_as::<_, DomainPlayer>("INSERT INTO player (...) VALUES (...) RETURNING *")
.bind(domain_player.id)
.bind(domain_player.shirt_number)
- .fetch_one(&mut *tx)
+ .fetch_one(&mut *tx.lock().await)
.await
.unwrap();
Note that because a &mut PgConenction is passed to shirt_get_next_and_increase inside the async block, which references the MutexGuard<Transaction<...>> returned by tx.lock().await, the MutexGuard will be held until shirt_get_next_and_increase completes, even while it yeilds (if it yields). This shouldn't be an issue in this code, since it appears to be mostly sequential, and does not access tx until shirt_get_next_and_increase has completed. If this is not what you want, (i.e. if your actual code does access tx concurrently while shirt_get_next_and_increase is running) you could instead have shirt_get_next_and_increase take a &Mutex<Transaction<...>>, and only have it hold the lock when it needs to access the connection.
An sketch of an alternate solution would be to restructure the code so that the Transaction is passed around by value in function arguments and return values, e.g.
pub type Lambda<'a, ArgT, ResT> =
dyn Fn(ArgT) -> Pin<Box<dyn Future<Output = Result<(ResT, ArgT), String>> + Send + 'a>> + Sync + 'a;
// ...
async fn shirt_get_next_and_increase<'a>(
&'a self,
mut tx: sqlx::Transaction<'static, sqlx::PgConnection>,
_model: String,
) -> Result<(i64, sqlx::Transaction<'static, sqlx::PgConnection>), String> {
// ...
Ok((value, tx))
}
This could resolve the borrowing and shared-mutability issues, but may make the API more cumbersome, and may be infeasible for other reasons.

How to resolve "dropped here while still borrowed" when creating a borrowing object in async context?

I try to create a transaction object from a parent connection being in async context. Here is a distilled situation:
use std::fmt::Debug;
use std::future::Future as StdFuture;
use std::pin::Pin;
use std::result::Result as StdResult;
#[derive(Debug)]
enum Error {
Unknown,
}
type Result<T> = StdResult<T, Error>;
type Future<'a, T> = Pin<Box<dyn StdFuture<Output = T> + Send + 'a>>;
trait DataConn<'c> {
type Tx: DataTx<'c>;
fn begin(&'c mut self) -> Future<Result<Self::Tx>>;
}
trait DataTx<'c> {
type Tx: DataTx<'c>;
fn begin(&'c mut self) -> Future<Result<Self::Tx>>;
}
async fn test_transactions<'c>(mut conn: impl DataConn<'c> + 'c)
{
let tx = conn.begin().await.unwrap();
}
As result I get the following error:
error[E0597]: `conn` does not live long enough
--> src/lib.rs:29:14
|
27 | async fn test_transactions<'c>(mut conn: impl DataConn<'c> + 'c)
| -- lifetime `'c` defined here
28 | {
29 | let tx = conn.begin().await.unwrap();
| ^^^^^^^^^^^^
| |
| borrowed value does not live long enough
| argument requires that `conn` is borrowed for `'c`
30 | }
| - `conn` dropped here while still borrowed
I don't get why the connection is going to be dropped before the transaction we create, while both have the same 'c lifetime. What do I miss here?
UPDATE: Some suggestions work being standalone, but cause compilation errors for my trait implementation:
struct SqliteConn<'c> {
conn: sqlx::SqliteConnection,
phantom: PhantomData<&'c ()>,
}
pub struct SqliteTx<'c> {
tx: Transaction<'c, Sqlite>,
}
impl<'c> DataConn<'c> for SqliteConn<'c> {
type Tx = SqliteTx<'c>;
fn begin(&'c mut self) -> Future<Result<Self::Tx>> {
Box::pin(async move {
Ok(SqliteTx {
tx: Connection::begin(&mut self.conn).await?,
})
})
}
}
You just need to annotate whatever necessary, just over the transaction lifetime (don't mess with self lifetimes here):
use std::fmt::Debug;
use std::future::Future as StdFuture;
use std::pin::Pin;
use std::result::Result as StdResult;
#[derive(Debug)]
enum Error {
Unknown,
}
type Result<T> = StdResult<T, Error>;
type Future<'a, T> = Pin<Box<dyn StdFuture<Output = T> + Send + 'a>>;
trait DataConn<'t> {
type Tx: DataTx<'t>;
fn begin(&mut self) -> Future<Result<Self::Tx>>;
}
trait DataTx<'t> {
type Tx: DataTx<'t>;
fn begin(&self) -> Future<Result<Self::Tx>>;
}
async fn test_transactions<'t>(mut conn: impl DataConn<'t>)
{
let tx = conn.begin().await.unwrap();
}
Playground

the trait `std::marker::Copy` is not implemented for Type

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?

How to tell the compiler a value is not used after awaiting in an async_trait method?

In the example code below, a non-send value, Vec<T>, is moved into a function that returns something else. At this point, I no longer care about that vector. The returned object stores no reference to it, it no longer exists.
However, when I .await on the next line I get the error "captured value is not Send". Which it isn't, but since it should have been destroyed when vector_as_string exited, it doesn't need to send it across threads when the future restarts, because that variable is never used again.
use async_trait::async_trait;
async fn write_value(value: Vec<u8>) {
println!("something")
}
fn vector_as_string<T>(vec: Vec<T>) -> Vec<u8> {
Vec::new()
}
#[async_trait]
trait Writer {
async fn write_array<T>(&mut self, value: Vec<T>);
}
pub struct WriterImplementation {}
#[async_trait]
impl Writer for WriterImplementation {
async fn write_array<T>(&mut self, value: Vec<T>) {
let string = vector_as_string(value);
write_value(string).await
}
}
#[tokio::main]
async fn main() {
println!("Hi");
}
Dependencies:
[dependencies]
tokio = { version = "1.9.0", features = ["full"]}
async-trait = "0.1.51"
Error:
error: future cannot be sent between threads safely
--> src/main.rs:20:55
|
20 | async fn write_array<T>(&mut self, value: Vec<T>) {
| _______________________________________________________^
21 | | let string = vector_as_string(value);
22 | |
23 | | write_value(string).await
24 | | }
| |_____^ future created by async block is not `Send`
|
note: captured value is not `Send`
--> src/main.rs:20:40
|
20 | async fn write_array<T>(&mut self, value: Vec<T>) {
| ^^^^^ has type `Vec<T>` which is not `Send`
= note: required for the cast to the object type `dyn Future<Output = ()> + Send`
help: consider further restricting this bound
|
20 | async fn write_array<T + std::marker::Send>(&mut self, value: Vec<T>) {
| ^^^^^^^^^^^^^^^^^^^
Adding T: Send as it suggests allows it to compile, but why does T need to be Send if we aren't holding any T's across the await, as it has already been moved?
From the async_trait documentation:
Async fns get transformed into methods that return Pin<Box<dyn Future + Send + 'async>> and delegate to a private async freestanding function.
Not all async traits need futures that are dyn Future + Send. To avoid having Send and Sync bounds placed on the async trait methods, invoke the async trait macro as #[async_trait(?Send)] on both the trait and the impl blocks.
Applied to your case:
#[async_trait(?Send)]
trait Writer {
async fn write_array<T>(&mut self, value: Vec<T>);
}
#[async_trait(?Send)]
impl Writer for WriterImplementation {
async fn write_array<T>(&mut self, value: Vec<T>) {
let string = vector_as_string(value);
write_value(string).await
}
}

Nested async function call causes lifetime issue

I feel like I am totally lost; what does "'1" mean in the error message?
error[E0597]: `o` does not live long enough
--> src/main.rs:32:19
|
31 | async fn foo6(&mut self, mut o: &'a mut Outer) {
| --------- lifetime `'1` appears in the type of `self`
32 | self.foo5(&mut o).await
| ----------^^^^^^-
| | |
| | borrowed value does not live long enough
| argument requires that `o` is borrowed for `'1`
33 | }
| - `o` dropped here while still borrowed
What can I do to make o live long enough? I think I'm using futures 03.
use futures::future::BoxFuture;
use futures::FutureExt;
use futures::stream::FuturesOrdered;
struct Inner {}
impl Inner {
async fn foo3(&mut self) -> Result<u32, ()> {
Ok(8)
}
}
// --- 1
struct Outer {
i: Inner,
}
impl Outer {
fn foo4(&mut self) -> BoxFuture<'_, Result<u32, ()>> {
self.i.foo3().boxed()
}
}
/// --- 2
struct Outer2<'a> {
futures_list: FuturesOrdered<BoxFuture<'a, Result<u32, ()>>>,
}
impl <'a> Outer2<'a> {
async fn foo6(&mut self, mut o: &'a mut Outer) {
self.foo5(&mut o).await
}
async fn foo5(&mut self, o: &'a mut Outer) {
self.futures_list.push(o.foo4());
}
}
#[tokio::main]
async fn main() {
let mut o = Outer { i: Inner {} };
let mut o2 = Outer2 { futures_list: FuturesOrdered::new() };
o2.foo5(&mut o).await;
}
playground
The '1 represents the anonymous lifetime of the self parameter for function foo5.
Note that in foo6, o is already a mutable reference to Outer, so writing &mut o actually gives you a mutable reference to a mutable reference to Outer with one too many indirections. You can fix your code by removing the extra reference:
async fn foo6(&mut self, mut o: &'a mut Outer) {
self.foo5(o).await
}
Playground

Resources