# Async Main and test function Pre-Design Meeting - Reference issue https://github.com/vincenzopalazzo/async-main-and-tests-initiative/issues/1 This document is part of the [async main initiative](https://vincenzopalazzo.github.io/async-main-and-tests-initiative) and the goal is to make a small summary of the historical issue and solution proposed to be able to support async main and test functions, with hope that we could make a good starting point for the initiative for the 2023. ## Table of Content - What is the goal of the initiative? - Where we are? - How my vision for support this feature looks like - Conclusion - Question and Consideration ## Async main and test goal We want to be able to run code like: ```rust= async fn foo() { todo!() } async fn main() { foo().await; } ``` Where currently we can have the same behavior as: ```rust= use rio_rt::runitime as rio; async fn foo() { todo!() } fn main() { rio::block_on(async { foo().await }); } ``` This is not a big deal, but have the possibility to have the entry point function marked with `async` it is important in the tests. We would like to have code like: ```rust= async fn foo() -> Result<(), ()> { todo!() } #[cfg(test)] mod test { #[test] async fn foo_test_one() { let foo = foo().await; assert!(foo.is_ok()); } } ``` Currently this is the most painful part because required to write some specific code to run the test, some runtime support built in macros like `#[tokio:test]`. This is where in my opinion have compiler support for `async` entry point is important. ## Where we are? - nice global executor blog post: https://without.boats/blog/global-executors/ - PR related to the `block_on`: https://github.com/rust-lang/rust/pull/65875 - `block_on`: need an rfc https://github.com/rust-lang/wg-async/issues/11 - We really need a global runtime? ## How my vision for support this feature looks like I do not start to play with it but my vision on this problem is that we could create a global interface that the runtime need to respect in order to inject the runtime in the rust compiler. In other words, at the actual state of the art we could write the following code: ```rust= // we could remove this? use rio_rt::runitime as rio; async fn foo() { todo!() } fn main() { rio::block_on(async { foo().await }); } ``` Where the user need to know the runtime API or just copy and paste somewhere the code. In my opinion we could get a deal with all the runtime that we had around the ecosystem by providing a trait that could give the API to the compiler to block on a function. An example could be ```rust= rio_rt::inject_runtime!(); async fn foo() { todo!() } async fn main() { foo().await; } ``` where the macros will include the following implementation ```rust= // Runtime trait that it is implemented inside the std. use std::rt::Rt; /// implement the runtime trait use rio_rt::runitime as rio; /// special function to create the runtime! // FIXME: we can use `Box<Rt>` pub fn new_rt() -> dyn* Rt { Runtime::new().unwrap() } /// from there it is code generated by the compiler. async fn __main() { foo().await; } fn main() { let rt = Runtime::new().unwrap(); rt.block_on(async { __main().await }); } ``` ### Final Notes This is a implementation that allow also the global executor to land in the compiler at some point, but this also allow to have API to swtich runtime without change any kind of code (maybe a single import? there is any way currently?) ## Conclusion - There are any blocking task or feature to have? - [Portable across runtimes](https://github.com/nrc/portable-interoperable); - Any others? - We could already implement this interface? - Maybe during the holidays I can implement something like that on rio. --- ## Question and Consideration ### (eholk) How invisible do we want this to be? Do we want the user to just be able to write ```rust async fn main() { println!("hello, world!"); } ``` and have it just work, or are we okay with a few lines of boilerplate to pick which executor we want? In other words, do we want it to be like allocation where as long as a crate that provides an allocator is linked in you can allocate, or do we want something more explicit? ## Extra pointers - https://tmandry.gitlab.io/blog/posts/2021-12-21-context-capabilities/ - https://docs.rs/tokio/latest/tokio/task/fn.consume_budget.html