11

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>?

dav_i
  • 27,509
  • 17
  • 104
  • 136

1 Answers1

12

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<(), _, _>.

Masklinn
  • 34,759
  • 3
  • 38
  • 57
rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • 2
    Also lingo correction: there is no *conversion* in the code posted, instead there is a *constraint*: `F` needs to implement the specified trait, but its identity doesn't change. – Masklinn Sep 10 '20 at 11:18