Errors

Using the ? operator propagates the error from the expression. How this works depends on whether ? is used from an async expression or from a handler. Using ? in an async expression propagates the error out of the async expression. This makes the output of the async expression a Result. Using ? from a handler immediately propagates the error out of the select! expression. Let’s look at the accept loop example again:

  1. use tokio::net::TcpListener;
  2. use tokio::sync::oneshot;
  3. use std::io;
  4. #[tokio::main]
  5. async fn main() -> io::Result<()> {
  6. // [setup `rx` oneshot channel]
  7. let listener = TcpListener::bind("localhost:3465").await?;
  8. tokio::select! {
  9. res = async {
  10. loop {
  11. let (socket, _) = listener.accept().await?;
  12. tokio::spawn(async move { process(socket) });
  13. }
  14. // Help the rust type inferencer out
  15. Ok::<_, io::Error>(())
  16. } => {
  17. res?;
  18. }
  19. _ = rx => {
  20. println!("terminating accept loop");
  21. }
  22. }
  23. Ok(())
  24. }

Notice listener.accept().await?. The ? operator propagates the error out of that expression and to the res binding. On an error, res will be set to Err(_). Then, in the handler, the ? operator is used again. The res? statement will propagate an error out of the main function.