Calling async function from main()

Good day folks,
I have a Tcp listener in an async function in lib.rs. I am calling this function from main.rs. However the function is never actually called. I have tried adding await to the call, but then main() needs to be async, but that is not allowed. What am I missing to make this call happen()? Thanks all.

main.rs

use std::error::Error;
use label_printer::Config;
use std::env;
use std::process;

fn main() -> Result<(), Box<dyn Error>> {
    let args: Vec<String> = env::args().collect();

    let config = Config::new(&args).unwrap_or_else(|err| {
        println!("Problem parsing arguments: {}", err);
        process::exit(1);
    });
    match label_printer::start_pos_listener(config) {
        Ok(x) => x,
        Err(_) => println!("Listener failure"),
    };
    Ok(())
   
}

lib.rs

use tokio::net::TcpListener;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use std::error::Error;

pub async fn start_pos_listener(config:Config) -> Result<(), Box<dyn Error>> {
    let mut pos = String::new();
    pos = pos + &config.pos_ip.trim() + ":" + &config.pos_port.trim();
    println!("{}", pos);
    let listener = TcpListener::bind(pos).await?;

    loop {
        let (mut socket, _) = listener.accept().await?;

        tokio::spawn(async move {
            let mut buf = [0; 1024];

            // In a loop, read data from the socket and write the data back.
            loop {
                let n = match socket.read(&mut buf).await {
                    // socket closed
                    Ok(n) if n == 0 => return,
                    Ok(n) => n,
                    Err(e) => {
                        eprintln!("failed to read from socket; err = {:?}", e);
                        return;
                    }
                };

                // Write the data back
                if let Err(e) = socket.write_all(&buf[0..n]).await {
                    eprintln!("failed to write to socket; err = {:?}", e);
                    return;
                }
            }
        });
    }

The easiest way is to make main async. To do this you need the #[tokio::main] macro.

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let args: Vec<String> = env::args().collect();

    let config = Config::new(&args).unwrap_or_else(|err| {
        println!("Problem parsing arguments: {}", err);
        process::exit(1);
    });
    match label_printer::start_pos_listener(config) {
        Ok(x) => x,
        Err(_) => println!("Listener failure"),
    };
    Ok(())
}

As an alternative to this, you can construct a Runtime object and use block_on.

fn main() -> Result<(), Box<dyn Error>> {
    let args: Vec<String> = env::args().collect();

    let config = Config::new(&args).unwrap_or_else(|err| {
        println!("Problem parsing arguments: {}", err);
        process::exit(1);
    });

    let runtime = tokio::runtime::Runtime::new().unwrap();

    match runtime.block_on(label_printer::start_pos_listener(config)) {
        Ok(x) => x,
        Err(_) => println!("Listener failure"),
    };
    Ok(())
}
1 Like

@alice Just for my edification, could you explain the #[tokio::main] entry. Clearly it is a reference to the tokio library but why not reference it with a use entry? Thanks

The #[tokio::main] macro is a macro defined by the Tokio crate that will transform your main function into something that immediately creates a new Runtime object and then calls block_on on the contents you actually wrote in the main function.

The reason you did not need to import it is that tokio::main is the full path. If you wanted to import it, you could do that:

use tokio::main;

#[main]
async fn main() -> Result<(), Box<dyn Error>> {
    ...
}
1 Like

I see. Cool. Thank you and have a good day.

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.