Writing an Accept Loop
Let's implement the scaffold of the server: a loop that binds a TCP socket to an address and starts accepting connections.
First of all, let's add required import boilerplate:
extern crate tokio;
use std::future::Future; // 1
use tokio::{
    io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, // 2
    net::{tcp::OwnedWriteHalf, TcpListener, TcpStream, ToSocketAddrs}, // 3
    sync::{mpsc, oneshot},
    task, // 4
};
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; // 5- Import traits required to work with futures.
- Import traits required to work with streams.
- For the socket type, we use TcpListenerfromtokio, which is similar to the syncstd::net::TcpListener, but is non-blocking and usesasyncAPI.
- The taskmodule roughly corresponds to thestd::threadmodule, but tasks are much lighter weight. A single thread can run many tasks.
- We will skip implementing detailed error handling in this example.
To propagate the errors, we will use a boxed error trait object.
Do you know that there's From<&'_ str> for Box<dyn Error>implementation in stdlib, which allows you to use strings with?operator?
Now we can write the server's accept loop:
extern crate tokio;
use tokio::net::{TcpListener, ToSocketAddrs};
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { // 1
    let listener = TcpListener::bind(addr).await?; // 2
    loop { // 3
        let (stream, _) = listener.accept().await?;
        // TODO
    }
    Ok(())
}- We mark the accept_loopfunction asasync, which allows us to use.awaitsyntax inside.
- TcpListener::bindcall returns a future, which we- .awaitto extract the- Result, and then- ?to get a- TcpListener. Note how- .awaitand- ?work nicely together. This is exactly how- std::net::TcpListenerworks, but with- .awaitadded.
- We generally use loopandbreakfor looping in Futures, that makes things easier down the line.
Finally, let's add main:
extern crate tokio;
use tokio::net::{ToSocketAddrs};
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> {
Ok(())
}
#[tokio::main]
pub(crate) async fn main() -> Result<()> {
    accept_loop("127.0.0.1:8080").await
}The crucial thing to realise is that in Rust, unlike in other languages, calling an async function does not run any code.
Async functions only construct futures, which are inert state machines.
To start stepping through the future state-machine in an async function, you should use .await.
In a non-async function, a way to execute a future is to hand it to the executor.