Function Currying that ultimately returns a Future

I'd like to curry this function that I will pass to another function to do work.

Here is an example.

fn find_by_name_cur<F>(conn: String) -> impl FnOnce(String) -> F
where
    F: Future<Output = Option<Item>>,
{
    move |name: String| async {
        println!(
            "fetching item by name: {} with connection: {}",
            name.clone(),
            conn.clone()
        );
        None
    }
}

I'm getting an error

error[E0308]: mismatched types
  --> src/main.rs:19:25
   |
15 |   fn find_by_name_cur<F>(conn: String) -> impl FnOnce(String) -> F
   |                       - this type parameter
...
19 |       move |name: String| async {
   |  _________________________^
20 | |         println!(
21 | |             "fetching item by name: {} with connection: {}",
22 | |             name.clone(),
...  |
25 | |         None
26 | |     }
   | |_____^ expected type parameter `F`, found opaque type
   |
   = note: expected type parameter `F`
                 found opaque type `impl Future`
   = help: type parameters must be constrained to match other types
   = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters

I'm not fully understanding what's going on here. I've also tried to implement this as a trait where the struct contains the connection info and the final function signature is String -> Future<Option<Item>> but that also didn't work.

Here is the playground if anyone can help.

Thanks!

This is somewhat similar to @Yandros 's answer here

So you can name an "existential type" or a Type Alias to an impl Trait outside of the function, and then say that your function returns that type.

Like this (note - the async block required a move too):

type FutReturn = impl Future<Output = Option<Item>>;

fn find_by_name_cur(conn: String) -> impl FnOnce(String) -> FutReturn
{
    move |name: String| async move {
        println!(
            "fetching item by name: {} with connection: {}",
            name.clone(),
            conn.clone()
        );
        None
    }
}

Here's a working playground

1 Like

Your playground link is not correct. (n.b. the http link was incorrect, not the contents)

1 Like

Here F is a type parameter. That means the caller can choose it freely. That's obviously incorrect: the return type depends on the implementation of your function, the caller can't choose it.

Consider the following function, which also fails to compile:

fn printable<T>() -> T
    where
        T: Display,
{
    "something"
}

"something" is a &str which does implement Display, so why doesn't this compile? Well, the reason is the same: T can be anything the caller chooses, so what if someone were to call this like printable::<u32>()? That wouldn't work.

1 Like

how so?

EDIT - nm, updated - thanks @quinedot !

Cool, I was hoping there would be a way to do this on stable without going nightly. I actually saw this solution on Stack Overflow. Thanks a lot for your help!

1 Like

This makes sense. The compiler is complaining at me when I try to return impl Future<Output = Option<Item>> as well though.

Here's a few other attempts

fn find_by_name_cur(conn: String) -> impl FnOnce(String) -> impl Future<Output = Option<Item>>
{
    move |name: String| async {
        println!(
            "fetching item by name: {} with connection: {}",
            name.clone(),
            conn.clone()
        );
        None
    }
}

yields

error[E0562]: `impl Trait` not allowed outside of function and inherent method return types
  --> src/main.rs:15:61
   |
15 | fn find_by_name_cur(conn: String) -> impl FnOnce(String) -> impl Future<Output = Option<Item>>
   |                                                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

and

fn find_by_name_cur(conn: String) -> impl FnOnce(String) -> dyn Future<Output = Option<Item>>
{
    move |name: String| async {
        println!(
            "fetching item by name: {} with connection: {}",
            name.clone(),
            conn.clone()
        );
        None as Option<Item>
    }
}

yields

error[E0277]: the size for values of type `(dyn Future<Output = Option<Item>> + 'static)` cannot be known at compilation time
  --> src/main.rs:17:25
   |
17 |     move |name: String| async {
   |                         ^ doesn't have a size known at compile-time
   |
   = help: the trait `Sized` is not implemented for `(dyn Future<Output = Option<Item>> + 'static)`
   = note: the return type of a function must have a statically known size

dyn Trait is unsized, so if you want to return it by value, you have to box it: the return type should be BoxFuture<Option<Item>>, and you should wrap the async block in Box::pin(…).

2 Likes

Yeeeaahhhhhhhh!!!!!!!!!!! It worked!!!!!! :smiley:

I feel invincible now! Thank you sooo much for your help!!!!!!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.