use std::future::Future;
struct A;
async fn foo<T, R>(t: T)
where
T: Fn(&mut A) -> R + Send + Sync + 'static,
R: Future + Send,
R::Output: Send
{
tokio::spawn(bar(t));
}
async fn bar<T, R>(t: T)
where
T: Fn(&mut A) -> R + Send,
R: Future + Send,
R::Output: Send
{
let mut a = A;
t(&mut a).await;
}
Playground : https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e8885b0f86a5b6defcbd2f45d8e35178
My instinct is that R only need to live as long as &mut A. But this is somehow not correct.
My goal is to provide a function that accept following code
async fn handle(&mut A) {
todo!()
}
async fn main() {
foo(handle).await
}
Related
I'm trying to write a multi-thread TCP server that can handle multiple connections at the same time with tokio.
I want to structure it in an event-driven way, where it's possible to attach one or more closures to a specific event (like new connection, message received, client disconnected etc).
For example:
server.on_message(|msg: String, stream: &mut TcpStream| {
async move {
println!("Recieved {:?}", msg);
stream.write_all(b"Hello\n").await;
}
}).await;
Every connection will receive its own thread which should have read access to a Vec of callbacks.
pub async fn run(&mut self) {
let listener = TcpListener::bind("127.0.0.1:9090").await.unwrap();
loop {
let (mut socket, _) = listener.accept().await.unwrap();
let cb = self.on_message.clone();
tokio::spawn(async move {
Self::process(socket, cb).await;
});
}
}
Unfortunately, my understanding of Rust is still very rudimentary and I run in circles with:
finding the right type to be stored in Vec
finding the right type for function argument that takes closure callback
Whenever I feel like I make progress in one place then I realise the other got messed up. This is the best I could do but it still doesn't work.
type Callback<T> = dyn Fn(T, &mut TcpStream) -> Pin<Box<dyn Future<Output=()> + Send>> + Send + 'static;
unsafe impl<T> Send for TcpStreamCallbackList<T> {}
unsafe impl<T> Sync for TcpStreamCallbackList<T> {}
impl<T> TcpStreamCallbackList<T> {
pub fn new() -> Self {
Self { callbacks: Vec::new() }
}
pub fn push<G: Send + 'static>(&mut self, mut fun: impl Fn(T, &mut TcpStream) -> G + Send + 'static) where G: Future<Output=()> {
self.callbacks.push(Arc::new(Box::new(move |val:T, stream: &mut TcpStream| Box::pin(fun(val, stream)))));
}
pub async fn call(&self, val: T, stream: &mut TcpStream) where T: Clone {
for cb in self.callbacks.iter() {
let _cb = cb.clone();
_cb(val.clone(), stream).await; // B O O M
}
}
}
The above code doesn't compile until I remove .await on the Future returned by callback in the call function (which defeats the purpose).
error[E0277]: `dyn for<'a> Fn(String, &'a mut tokio::net::TcpStream) -> Pin<Box<dyn futures::Future<Output = ()> + std::marker::Send>> + std::marker::Send` cannot be shared between threads safely
--> src/main.rs:94:26
From what I understand the problem is that the retuned Future is not Send.
note: required by a bound in `tokio::spawn`
--> /Users/lukasz/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.25.0/src/task/spawn.rs:163:21
|
163 | T: Future + Send + 'static,
| ^^^^ required by this bound in `tokio::spawn`
I have no idea if my type makes sense and is thread-safe. I also don't know why the compiler thinks the return type is not Send. I'm really stuck here and would appreciate any help.
I've put together one that is slightly simpler but can be spawned (playground). The key is that Callback needs to be Sync in order for &self to be Send. I tried to use the trick mentioned in this comment, but it doesn't appear to work here, nor does making call take &mut self. I wrote more about Send and Sync on that answer.
use std::future::Future;
use std::pin::Pin;
type CallbackFuture<O> = Pin<Box<dyn Future<Output = O> + Send>>;
type Callback<T> = dyn (Fn(T) -> CallbackFuture<()>) + Send + Sync;
pub struct CallbackList<T> {
list: Vec<Box<Callback<T>>>,
}
impl<T> CallbackList<T> {
pub fn new() -> Self {
Self { list: Vec::new() }
}
pub fn push<F>(&mut self, f: F)
where
F: Fn(T) -> CallbackFuture<()>,
F: Send + Sync + 'static,
{
self.list.push(Box::new(f))
}
pub async fn call(&self, t: T)
where
T: Clone,
{
for f in &self.list {
f(t.clone()).await;
}
}
}
#[tokio::main]
async fn main() {
let mut calls = CallbackList::new();
calls.push(|i| {
Box::pin(async move {
println!("{i}");
})
});
calls.push(|i| {
Box::pin(async move {
println!("{}", i + 1);
})
});
let handle = tokio::spawn(async move {
calls.call(34).await;
});
handle.await.unwrap();
}
I have removed as many trait bounds, 'statics, and wrappers as possible, but you may need to add some back depending on what you do with it. Right now it takes T, but it should be possible to separate that into T and &mut TcpStream. If you update your question with a main function that uses all the elements, I can change mine to match. If all else fails, you can use (_, Arc<Mutex<TcpStream>>) as T.
The following code provides a container in which to store prepared functions for asynchronous code so that you can pass it somewhere for execution:
use std::collections::HashMap;
use futures::future::{Future, BoxFuture};
trait AsyncFn {
fn call(&self, arg: String) -> BoxFuture<'static, ()>;
}
impl<T, F> AsyncFn for T
where
T: Fn(String) -> F,
F: Future<Output = ()> + 'static + Send,
{
fn call(&self, arg: String) -> BoxFuture<'static, ()> {
Box::pin(self(arg))
}
}
async fn async_test(data: String) -> () {
println!("{data}");
}
#[async_std::main]
async fn main() {
let mut callables: HashMap<String, Box<dyn AsyncFn>> = HashMap::new();
callables.insert("test_func".to_string(), Box::new(async_test));
let awaitable = callables.get("test_func").unwrap();
awaitable.call("test string argument1".to_string()).await;
let awaitable = callables.get("test_func").unwrap();
awaitable.call("test string argument2".to_string()).await;
}
But this greatly limits the set of functionality called in this way. I can only pass strings.
Is it possible to describe the trait more universally, so that AsyncFn can accept a heap reference to any type?
I tried to do it like this:
trait AsyncFn<B> {
fn call(&self, arg: Box<B>) -> BoxFuture<'static, ()>;
}
impl<T, B, F> AsyncFn<B> for T
where
T: Fn(Box<B>) -> F,
F: Future<Output = ()> + 'static + Send,
{
fn call(&self, arg: Box<B>) -> BoxFuture<'static, ()> {
Box::pin(self(arg))
}
}
async fn async_test(data: Box<String>) -> () {
println!("String data = {data}");
}
async fn async_test2(data: Box<u64>) -> () {
println!("u64 data = {data}");
}
But this still restricts the type that I send to the container:
let mut callables: HashMap<String, Box<dyn AsyncFn<String>>> = HashMap::new();
callables.insert("test_func".to_string(), Box::new(async_test));
// ! callables.insert("test_func2".to_string(), Box::new(async_test2));
I should probably try wrapping this in an enum or some other type, but maybe there is a more elegant solution?
I want to write a functions that can accept three callback types, like so:
pub enum Callback<T> {
Regular(Box<dyn FnMut(T) + Send>),
Boxed(Box<dyn FnMut(Box<T>) + Send>),
RegularWithInt(Box<dyn FnMut(T, i32) + Send>),
}
But the caller of the function should not need to use the Callback enum, but simply be allowed to pass in a function or closure. This is what I came up with, taking some inspiration from Bevy's system functions:
// Helper trait for IntoCallback.
//
// For each tuple of args, it provides conversion from a function with
// these args to the correct enum variant.
trait ArgTuple<T> {
type Func;
fn convert(func: Self::Func) -> Callback<T>;
}
impl<T> ArgTuple<T> for (T,) {
type Func = Box<dyn FnMut(T) + Send>;
fn convert(func: Self::Func) -> Callback<T> {
Callback::Regular(func)
}
}
impl<T> ArgTuple<T> for (Box<T>,) {
type Func = Box<dyn FnMut(Box<T>) + Send>;
fn convert(func: Self::Func) -> Callback<T> {
Callback::Boxed(func)
}
}
impl<T> ArgTuple<T> for (T, i32) {
type Func = Box<dyn FnMut(T, i32) + Send>;
fn convert(func: Self::Func) -> Callback<T> {
Callback::RegularWithInt(func)
}
}
pub trait IntoCallback<T, Args>: Send {
fn into_callback(self) -> Callback<T>;
}
impl<T, A0, Func> IntoCallback<T, (A0,)> for Func
where
Func: FnMut(A0) + Send + 'static,
(A0,): ArgTuple<T, Func = Box<dyn FnMut(A0)>>,
{
fn into_callback(self) -> Callback<T> {
<(A0,) as ArgTuple<T>>::convert(Box::new(self))
}
}
impl<T, A0, A1, Func> IntoCallback<T, (A0, A1)> for Func
where
Func: FnMut(A0, A1) + Send + 'static,
(A0, A1): ArgTuple<T, Func = Box<dyn FnMut(A0, A1)>>,
{
fn into_callback(self) -> Callback<T> {
<(A0, A1) as ArgTuple<T>>::convert(Box::new(self))
}
}
However, rustc informs me that the callbacks I thought would implement IntoCallback don't in fact do so.
#[test]
fn callback_conversion() {
let cb = |s: String| {};
assert!(matches!(cb.into_callback(), Callback::Regular(_))); // Compiler error, "perhaps you need to implement IntoCallback?"
}
Where is the error? Is there a trick to get rustc to explain why this trait is not implemented? I already checked that (String,) implements ArgTuple<String>, as expected.
An example from actix-web is as follows:
use actix_web::{web, App, Responder, HttpServer};
async fn index() -> impl Responder {
"Hello world!"
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new().service(
web::scope("/app").route("/index.html", web::get().to(index)),
)
})
.bind("127.0.0.1:8088")?
.run()
.await
}
My question is around how the statement to(index) works in Rust.
Looking at the source code for to we see:
pub fn to<F, T, R, U>(mut self, handler: F) -> Self
where
F: Factory<T, R, U>,
// --- snip
Where Factory is defined as:
pub trait Factory<T, R, O>: Clone + 'static
where
R: Future<Output = O>,
O: Responder,
{
fn call(&self, param: T) -> R;
}
What is the mechanism by which the function async fn index() -> impl Responder gets converted to Factory<T, R, O>?
There is an implementation of the trait just after your snippet:
impl<F, R, O> Factory<(), R, O> for F
where
F: Fn() -> R + Clone + 'static,
R: Future<Output = O>,
O: Responder,
{
fn call(&self, _: ()) -> R {
(self)()
}
}
This can be read as: if a type F implements Fn() -> Future<Output = impl Responder> + ... then it also implements Factory<(), _, _>.
And an async fn is syntactic sugar for a function which returns a Future of some sort (and can use .await internally), so async fn index() -> impl Responder implements Fn() -> impl Future<Output = impl Responder> so it also implements Factory<(), _, _>.
Can I accept a Box<dyn Error + Send> in places that accept Box<dyn Error>? If yes, how? If no, why, and is there a more elegant way to do it than in the following example?
#![allow(unused)]
use std::error::Error as StdError;
use std::result::Result as StdResult;
type Result = StdResult<(), Box< dyn StdError >>;
type SndResult = StdResult<(), Box< dyn StdError + Send >>;
fn fn_returning_result() -> Result { Ok(()) }
fn fn_returning_sndresult() -> SndResult { Ok(()) }
/// Accept a callback that returns a non-Send `Result`.
fn register_callback<CB: FnOnce() -> Result>(cb: CB) { /* ... */ }
fn main() -> Result {
// Is there a way to get rid of ... vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv ... this part?
let cb = || fn_returning_sndresult().map_err(|e| -> Box<dyn StdError> { e });
register_callback(cb);
Ok(())
}
Playground
Edit: What I would like to do here is just let cb = || fn_returning_sndresult();, but that won't compile.
Also, I'd like to call either fn_returning_result()? or fn_returning_sndresult()? inside the callback with a single return type and without doing a map_err.
This is based on an answer I got on Reddit by Llaurence_:
I can change register_callback to accept both Box<dyn Error> and Box<dyn Error + Send> errors by making the error type generic:
fn register_callback<CB, E>(cb: CB)
where
CB: FnOnce() -> StdResult<(), Box<E>>,
E: StdError + ?Sized,
{ /* ... */ }
Now I can pass a callback that returns either kind of error / result:
register_callback(fn_returning_result);
register_callback(fn_returning_sndresult);
Q: Can I accept a Box<dyn Error + Send> in places that accept Box<dyn Error>?
A: Yes, with some newtype wrapping. The solution I finally ended up with was this: (Playground)
use std::error::Error as StdError;
use std::result::Result as StdResult;
use std::fmt;
type Result = StdResult<(), Box< dyn StdError >>;
type SndResult = StdResult<(), Box< dyn StdError + Send >>;
fn fn_returning_result() -> Result { dbg!(Err("oops".into())) }
fn fn_returning_sndresult() -> SndResult { dbg!(Ok(())) }
/// Result type using `Er` defined below.
type Rt<T = ()> = StdResult<T, Er>;
// Error wrapper
#[derive(Debug)]
struct Er(Box<dyn StdError>);
impl fmt::Display for Er {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
impl StdError for Er {}
// Allow `?` operator
impl From<Box<dyn StdError>> for Er {
fn from(err: Box<dyn StdError>) -> Self {
Er(err)
}
}
impl From<Box<dyn StdError + Send>> for Er {
fn from(err: Box<dyn StdError + Send>) -> Self {
Er(err)
}
}
// List of callbacks
struct Callbacks<'a>(Vec<Box<dyn FnOnce() -> Rt + 'a>>);
impl<'a> Callbacks<'a> {
fn new() -> Self {
Callbacks(Vec::new())
}
fn add(&mut self, cb: impl FnOnce() -> Rt + 'a) {
self.0.push(Box::new(cb));
}
fn pop_and_call(&mut self) -> Rt {
self.0.pop().unwrap()()
}
}
// Example
fn main() -> Result {
let mut callbacks = Callbacks::new();
callbacks.add(|| { Ok(fn_returning_result()?) });
callbacks.add(|| { Ok(fn_returning_sndresult()?) });
callbacks.pop_and_call()?;
callbacks.pop_and_call()?;
Ok(())
}
Note that the callbacks can use ? on either Result or SndResult values (thanks to the From trait impls), and that their return values are valid Results as well, allowing main to also use ? on their calls (thanks to the StdError trait impl).