How to receive a async callback?

I'm trying to receive a async callback, I have this simple example

#![allow(unused_variables, unused_imports, dead_code)]
use futures::future::{FutureExt, LocalBoxFuture};
use async_std::prelude::*;
use async_std::fs::{read_dir, DirEntry};
use async_std::path::Path;
use tokio::prelude::*;


async fn foo(message: String, cb: &dyn Fn(String) -> dyn Future<Output=()>) {
    cb(message).await;
}


#[tokio::main]
async fn main() {
}

Which obviously won't compile

src/main.rs|10 col 5 error 277| the size for values of type `dyn core::future::future::Future<Output = ()>` cannot be known at compilation time
||    |
|| 10 |     cb(message).await;
||    |     ^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
||    |
||    = help: the trait `std::marker::Sized` is not implemented for `dyn core::future::future::Future<Output = ()>`
||    = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
||    = note: all local variables must have a statically known size
||    = help: unsized locals are gated as an unstable feature
|| For more information about this error, try `rustc --explain E0277`.

I can't understand how it doesn't know the size. Since is a reference it would be an address size (I'm trying my C background here), but I really can't understand this error, what I'm missing?

Regards

The callback returns dyn Future<Output=()> which is unsized type. Functions cannot return unsized types.

1 Like

Yeah, it isn't the closure that it doesn't know the size of. It's the return value of that closure. Typically we would use generics in this case:

async fn foo<F, Fut>(message: String, cb: F)
where
    F: Fn(String) -> Fut,
    Fut: Future<Output = ()>,
{
    cb(message).await;
}

Additionally I noticed that you're importing both Tokio and async-std. You should not be mixing the two runtimes, and I recommend you use Tokio.

1 Like

I put dyn in the return type because impl Future<()> was not possible there.

The generics are a got catch, so you're delaying the type resolution to "call time", nice trick, I was struggling with dyn vs impl. My understand is that impl is solved at compiled time "I'm returning something the implements trait Foo and that implementation exists and is track-able at compile time", while dyn means "Well this may vary and can only be know at compile time", thinks that depend on user input are a good example of dyn stuff...

I never though about generics, they are like removing the type responsibility from the function writer to the caller. Is this right? So if I have a function that I don't know what will return (and it can't be dyn because is unsized) I can used generics to delay the decision to the function call

Thanks again guys for taking your time to answer this :smiley:

Regards

The difference with impl vs dyn is that impl is turned into a concrete type at compile time, whereas dyn stays unknown even at compile time, and dynamic dispatch is used at runtime to figure out the actual type. To be clear, dyn is not known at compile time.

Additionally it seems like you're thinking of impl in an argument as a different thing from generics, but it isn't. Using impl in an argument to the function is still using generics, it's just more hidden. On the other hand, using impl in the return value is a different thing from generics, because in that case the function author chooses the concrete type.

2 Likes

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