File IO

Create Files and Directories

tokio-badge std-badge

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.

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(())
}

Add tokio to Cargo.toml with the macros and fs features enabled.

[dependencies]
tokio = { version = "*", features = ["macros", "fs"] }

Read files

tokio-badge std-badge

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.
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(())
}

Add tokio to Cargo.toml with the macros and fs features enabled.

[dependencies]
tokio = { version = "*", features = ["macros", "fs"] }

Write Files

tokio-badge std-badge

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.

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(())
}

Add tokio to Cargo.toml with the macros and fs features enabled.

[dependencies]
tokio = { version = "*", features = ["macros", "fs"] }

Remove Files and Directories

tokio-badge std-badge

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.

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(())
}

Add tokio to Cargo.toml with the macros and fs features enabled.

[dependencies]
tokio = { version = "*", features = ["macros", "fs"] }

AsyncRead and AsyncWrite

tokio-badge std-badge

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.
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(())
}

Add tokio to Cargo.toml with the macros, io-util and fs features enabled.

[dependencies]
tokio = { version = "*", features = ["macros", "fs", "io-util"] }