Passing async fn (returning an `anyhow::Result`) as function parameter

I have the following async fn signature:

pub async fn serve<
       R: tokio::io::AsyncRead + Unpin + Send,
       W: tokio::io::AsyncWrite + Unpin + Send> (
    mut reader: R,
    mut writer: W,
) -> anyhow::Result<()> {
...
...
Ok(())
}

When I want to pass this function as a function argument, i.e.

 pub async fn run(
        &mut self,
        serve: impl Fn(tokio::io::AsyncRead + Unpin + Send,
                                  tokio::io::AsyncWrite + Unpin + Send) ->
               futures::Future<Output=anyhow::Result<()>>,
    ) -> anyhow::Result<()> {

I get the following error msg:

error[E0277]: the size for values of type `(dyn core::future::future::Future<Output = std::result::Result<(), anyhow::Error>> + 'static)` cannot be known at compilation time
   --> src/bin/sxsim/control/process/appmngr.rs:144:9
    |
144 |         futures::Future<Output=anyhow::Result<()>>,
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
    | 
   ::: /home/hjansen/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore/ops/function.rs:228:5
    |
228 |     type Output;
    |     ------------ required by this bound in `std::ops::FnOnce`
    |
    = help: the trait `std::marker::Sized` is not implemented for `(dyn core::future::future::Future<Output = std::result::Result<(), anyhow::Error>> + 'static)`

Since anyhow is widely used I would believe there is an idiomatic way to resolve this... Help/guidance appreciated! Thanks.

Btw: this is a follow up on Concrete types of let (reader, writer) = split(stream);

I think you're missing one level of indirection. The function returns some type which implements future::Future, not the trait itself. You can fix that with this:

pub async fn run<T>(
    &mut self,
    serve: impl Fn(tokio::io::AsyncRead + Unpin + Send, tokio::io::AsyncWrite + Unpin + Send) -> T,
) -> anyhow::Result<()>
where
    T: futures::Future<Output = anyhow::Result<()>>,
{

However, that'll bring you to another set of errors relating to how serve takes in generic parameters, and you've declared them again as concrete traits. I don't know if there's as easy of a fix here - Fn closures can't be generic over types.

Maybe you could change serve to take in Box<dyn tokio::io::AsyncRead + Unpin + Send> and the same for AsyncWrite, rather than a generic? Then it itself wouldn't be generic, and you could pass it as an Fn(Box<dyn ...>, Box<dyn ...>) -> T.

Got it solved:

use tokio;

/// Service detail.
pub async fn serve<
    R: tokio::AsyncRead  + Unpin + Sync + Send + 'static,
    W: tokio::AsyncWrite + Unpin + Sync + Send + 'static
    > (
    //  Notice: adding the `Unpin` trait had the same effect as starting `serve` with
    // `tokio::pin!(reader, writer);`
    mut reader: R,
    mut writer: W,
    ) -> anyhow::Result<()> {
        loop { ... };
        Ok(())
    }

/// Service runner.
async fn run<F, Fut>
    (
        &mut self,
        serve: F,  
    ) -> anyhow::Result<()>
        where
            F: 'static + Copy + FnOnce(
                Box<dyn tokio::AsyncRead + Unpin + Sync + Send>,
                Box<dyn tokio::AsyncWrite + Unpin + Sync + Send>,
                AConfig) -> Fut + Send,
            Fut: futures::Future<Output=anyhow::Result<()>> + Send + 'static,
    {
        ...;

        let (mut reader, mut writer) = split(stream);
        tokio::spawn(
                serve( Box::new(reader), Box::new(writer) )
        );
        ...;
    }
1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.