How to achieve function overloading like Actix-web?

Hello

In Actix-web one can define routes like this:

  .service(
      web::resource("/my-page")
          .route(web::get().to(my_page_function)),
  )

my_page_function could have any of these signatures:

pub async fn my_page_function(session: Session, mysql: web::Data<MySQL>, req: HttpRequest) -> Result<HttpResponse>;

// or

pub async fn my_page_function(req: HttpRequest) -> Result<HttpResponse>;

// or

pub async fn my_page_function(session: Session) -> Result<HttpResponse>;

// ...

Depending on if you need the session, request, or MySQL-client in your function. So even while Rust does not support function overloading, Actix-web is kinda doing it. I think Actix achieves this by using app_data:

 .app_data(web::Data::clone(&mysql))

You can add as many app_data as you want and it is available in all your route functions.

How would I implement something like this myself?

I have a basic http server with routes:

// Defined types
//
// Type for PostgreSQL-wrapper.
type Psql = Arc<Mutex<Client>>;

// Type for API-wrapper.
type Api = Arc<Mutex<dyn ExchangeAPI>>;

// Type for tuple containing routes. (HTTP-method, path, fn pointer to route function).
type Route = (
    &'static str,
    &'static str,
    for<'a> fn(&'a Routes, Http, &'a mut TcpStream, Psql, Api)
        -> BoxFuture<'a, Result<HttpResponse, String>>
);


// Macros
//
// This macro improves the readability of the code. Instead of
// ("GET", "/", |r, h| Routes::index(r, h).boxed()) the programmer can just
// type route!("GET", "/", Routes::index).
macro_rules! route {
    ($m:expr, $p:expr, $fn:path) => {
        ($m, $p, |r, h, s, c, a| $fn(r, h, s, c, a).boxed())
    }
}

  // Define all your routes here.
  let routes: Arc<std::vec::Vec<Route>> = Arc::new(vec![
      route!("GET", "/", Routes::index),
      route!("GET", "/welcome/{name}", Routes::welcome),
      route!("GET", "/welcome/{name}/{age}", Routes::welcome_age),
      route!("POST", "/print", Routes::print_name),
      route!("GET", "/html-page", Routes::html_page),
      route!("GET", "/ws", Routes::websocket),
  ]);

And a function looks like this:

    pub async fn my_page_function(&self, req: Http, stream: &mut TcpStream, psql: Psql, api: Api) -> Result<HttpResponse, String>;

I have to include req, stream, psql and api in the signature because at least one of the route functions need it. But some route functions don't even need one of those. So how can I edit my code so I don't have to pass unneeded parameters to route functions not needing specific items?

2 Likes

I am aware of Axum. But I am making a project for learning purposes and want to do as much as possible myself without using to many libraries.

The pattern is the same between them. The link above is a learning material about the pattern, not an advocation to use axum.

2 Likes