Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

File IO

Create Files and Directories

tokio Asynchronous

Creating files and directories on disk takes time. Tokio provides non-blocking versions of these operations so your program does not have to stop and wait while the work is done.

File::create creates a new file. If the file already exists, it is overwritten. create_dir creates a single directory. If it already exists, it fails. create_dir_all creates a directory and any missing parent directories along the path.

This recipe requires the macros and fs feature flags of tokio to be enabled in Cargo.toml.

use std::io;
use tokio::fs::File;

#[tokio::main]
async fn main() -> io::Result<()> {
    // create a file
    File::create("data.txt").await?;

    // create a single directory
    tokio::fs::create_dir("my_dir").await?;

    // create directory and missing parents
    tokio::fs::create_dir_all("my_dir/sub_dir/inner").await?;

    Ok(())
}

Read files

tokio Asynchronous

Reading a file from disk takes time. Tokio provides non-blocking versions of file reads so your program can keep doing other work while waiting for the data to come back.

  • read loads the file into raw bytes. Useful when you need to process the data directly.
  • read_to_string loads the file into plain text. Useful when you know the file contains readable characters.

This recipe requires the macros and fs feature flags of tokio to be enabled in Cargo.toml.

use std::io;

fn process_data(data: &[u8]) {
    println!("Data Length: {}", data.len());
}

#[tokio::main]
async fn main() -> io::Result<()> {
    // read to a Vec<u8>
    let content = tokio::fs::read("data.txt").await?;
    process_data(&content);

    // read to a String
    let str_content = tokio::fs::read_to_string("data.txt").await?;
    process_data(str_content.as_bytes());

    Ok(())
}

Write Files

tokio Asynchronous

Writing to disk takes time. tokio::fs::write is a non-blocking way to write bytes to a file. It creates the file if it does not exist, or overwrites it if it does.

This recipe requires the macros and fs feature flags of tokio to be enabled in Cargo.toml.

use std::io;

#[tokio::main]
async fn main() -> io::Result<()> {
    let content = b"Generic data from program!";
    tokio::fs::write("data.txt", content).await?;

    Ok(())
}

Remove Files and Directories

tokio Asynchronous

Deleting from disk takes time. Tokio provides non-blocking versions of these operations so your program does not have to stop and wait while the work is done.

This recipe requires the macros and fs feature flags of tokio to be enabled in Cargo.toml.

use std::io;

#[tokio::main]
async fn main() -> io::Result<()> {
    // remove a file
    tokio::fs::remove_file("data.txt").await?;

    // remove an empty directory
    tokio::fs::remove_dir("my_dir").await?;

    // remove a directory and all its contents
    tokio::fs::remove_dir_all("my_dir").await?;

    Ok(())
}

AsyncRead and AsyncWrite

tokio Asynchronous

AsyncRead and AsyncWrite are the building blocks for all non-blocking I/O in Tokio. Any type that can be read from or written to non-blocking i.e file, a network stream, or a buffer, implements one or both of these.

Their extensions AsyncReadExt and AsyncWriteExt add convenient methods on top:

  • write_all writes bytes to a writer.
  • read_to_end reads everything from a reader into a buffer.
  • copy streams data from a reader directly into a writer.

This recipe requires the macros, fs and io-util feature flags of tokio to be enabled in Cargo.toml.

use std::io;
use tokio::fs::File;
use tokio::io::{AsyncReadExt, AsyncWriteExt};

#[tokio::main]
async fn main() -> io::Result<()> {
    // AsyncWrite: write bytes to a file
    let mut file = File::create("data.txt").await?;
    file.write_all(b"Generated Data!").await?;

    // AsyncRead: read bytes from a file
    let mut file = File::open("data.txt").await?;
    let mut contents = Vec::new();
    file.read_to_end(&mut contents).await?;
    println!("Data Length: {}", contents.len());

    // stream from reader to writer
    let mut reader = File::open("data.txt").await?;
    let mut writer = File::create("copy.txt").await?;
    tokio::io::copy(&mut reader, &mut writer).await?;

    Ok(())
}