Many system languages have basic support for I/O operations but often do not make a clear distinction between synchronous and asynchronous approaches. Rust was designed from the ground up to ensure safety and performance, including when working with I/O. Therefore, the Rust standard library (std) implements only synchronous I/O, while popular asynchronous support is provided through separate external libraries (e.g., tokio, async-std).
Mixing synchronous and asynchronous I/O approaches often leads to maintainability issues and problems with safety and performance, as these approaches have different resource, thread, and blocking models. For example, directly reading a large file or waiting for data from the network can block the execution thread (including the main one), slowing down the application.
Rust offers a clear separation: The standard library (std::io) — only synchronous I/O with safe error handling and strict control over resource ownership. For asynchronous solutions, external libraries and asynchronous runtimes are used — they provide their types and APIs but with similar error and safety semantics.
Example of synchronous file reading code:
use std::fs::File; use std::io::{self, Read}; fn main() -> io::Result<()> { let mut file = File::open("foo.txt")?; let mut contents = String::new(); file.read_to_string(&mut contents)?; println!("{}", contents); Ok(()) }
Example of asynchronous file reading code using tokio:
use tokio::fs::File; use tokio::io::AsyncReadExt; #[tokio::main] async fn main() -> tokio::io::Result<()> { let mut file = File::open("foo.txt").await?; let mut contents = String::new(); file.read_to_string(&mut contents).await?; println!("{}", contents); Ok(()) }
Key features:
Can types be mixed directly (e.g., File from std and File from tokio) for passing between synchronous and asynchronous functions?
No. They are incompatible at the API level, and standard types do not implement asynchronous traits.
Does std::thread::spawn block in an asynchronous function if synchronous I/O is called within it?
Yes. If synchronous I/O is invoked in an asynchronous environment, the thread will be blocked, negating the benefits of asynchronicity.
Can async fn main be used without a runtime (tokio or async-std)?
No. The asynchronous entry point must be executed with a specific runtime; otherwise, the compiler will not allow using async fn main.
In a multi-threaded server written in Rust, synchronous reading from std::io is used in asynchronous request handlers. As a result, the load blocks the event loop, increasing latencies, and the server struggles under peak loads.
Pros:
Cons:
Only asynchronous types are used for all file and network operations within asynchronous code, and types are strictly monitored while errors are caught through Result.
Pros:
Cons: