Ben Chuanlong Du's Blog

It is never too late to learn.

IO in Rust

Things on this page are fragmentary and immature notes/thoughts of the author. Please read with your own judgement!

In [ ]:
:timing
:sccache 1

Tips and Traps

  1. Use buffered IO as much as possible (for performance concerns). However, be aware that BufWriter might not write data into a file until BufWriter.flush is called. This is especially tricky if you explore Rust code in a JupyterLab notebook. If you use BufWriter in a JupyterLab notebook, it is suggested that you either call BufWriter.flush manually

     :::Rust
     let f = File::create("stat.csv").expect("Unable to create file");
     let mut bfw = BufWriter::new(f);
     for (id, n, num_jokers, rank, htype) in &stat {
         bfw.write(format!("{id},{n},{num_jokers},{rank},{htype}\n").as_bytes());
     }
     bfw.flush()
    
    

    or you enclose BufWriter in a smaller curly brace scope (to rely on the Drop check to trigger flush).

     :::Rust
     {
       let f = File::create("stat.csv").expect("Unable to create file");
       let mut bfw = BufWriter::new(f);
       for (id, n, num_jokers, rank, htype) in &stat {
           bfw.write(format!("{id},{n},{num_jokers},{rank},{htype}\n").as_bytes());
       }
     }
  2. Key strokes CTRL + D signals an EOF to stdin input.

  3. Methods reading from stdin appends the input to the buffer rather than overwrite it!

  4. Rust crates serde and cron are popular serialization/deserialization libraries.

In [7]:
use std::path::Path;
use std::io::BufReader;
use std::fs::File;
use std::io::Read;
use std::io::Lines;
In [8]:
use std::io::{self, BufReader, BufWriter};
use std::io::prelude::*;
use std::fs::File;
In [4]:
use std::fs;
In [5]:
fs::read_to_string("data.txt")
Out[5]:
Err(Os { code: 2, kind: NotFound, message: "No such file or directory" })

std::fs::read

Read the entire contents of a file into a bytes vector. This is a convenience function for using File::open and read_to_end with fewer imports and without an intermediate variable. It pre-allocates a buffer based on the file size when available, so it is generally faster than reading into a vector created with Vec::new().

In [19]:
fs::read("data.txt")
Out[19]:
Ok([104, 111, 119, 10, 97, 114, 101, 32, 121, 111, 117, 10, 100, 111, 105, 110, 103])
In [13]:
fs::write("o1.txt", "this is an example line output")
Out[13]:
Ok(())

std::fs::File

You can open a file from both a Path and a string.

In [14]:
let f1 = File::open(Path::new("data.txt"));
In [15]:
f1
Out[15]:
Ok(File { fd: 3, path: "/workdir/archives/blog/misc/content/2020/11/rust-io/data.txt", read: true, write: false })
In [16]:
let f2 = File::open("data.txt");
In [17]:
f2
Out[17]:
Ok(File { fd: 4, path: "/workdir/archives/blog/misc/content/2020/11/rust-io/data.txt", read: true, write: false })

Read text from a file.

In [19]:
let mut f = File::open("data.txt")?;
In [20]:
let mut content = String::new();
f.read_to_string(&mut content)?;
In [21]:
content
Out[21]:
"how\nare you\ndoing"

Write text into a file.

In [8]:
let mut file = File::create("output.txt").unwrap();
file
Out[8]:
File { fd: 4, path: "/workdir/archives/blog/misc/content/2020/11/rust-io/output.txt", read: false, write: true }
In [9]:
file.write_all(b"Hello, world!");

std::io::BufReader

In [29]:
let br = BufReader::new(File::open("data.txt")?);
for line in br.lines() {
    println!("{:?}", line);
}
Ok("how")
Ok("")
Ok("are you")
Ok("doing")
Out[29]:
()
In [31]:
let br = BufReader::new(File::open("data.txt")?);
for (idx, line) in br.lines().enumerate() {
    println!("Line {}: {:?}", idx, line);
}
Line 0: Ok("how")
Line 1: Ok("")
Line 2: Ok("are you")
Line 3: Ok("doing")
Out[31]:
()
In [33]:
let br = BufReader::new(File::open("data.txt")?);
for (idx, line) in br.lines().enumerate() {
    println!("Line {}: {:?}", idx, line);
}
Line 0: Ok("how")
Line 1: Ok("")
Line 2: Ok("are you")
Line 3: Ok("doing")
Out[33]:
()

std::io::BufWriter

In [16]:
let f = File::create("o2.txt").expect("Unable to create file");
let mut bfw = BufWriter::new(f);
bfw.write_all(b"writing data\nusing std::io::BufWriter")
Out[16]:
Ok(())

You have to call the method BufWriter.flush to force the buffer to output immediately.

In [19]:
bfw.flush()
Out[19]:
Ok(())

Before a buffer is destructed, its content is output to the file.

In [18]:
{
    let f = File::create("o3.txt").expect("Unable to create file");
    let mut bfw = BufWriter::new(f);
    bfw.write_all(b"writing data\nusing std::io::BufWriter")
}
Out[18]:
Ok(())

Read From Stdin

Stdin::read_line reads a line (terminated by \n) and returns a Result object. On success, it returns Ok(n) where n is the number of bytes read from stdin.

In [2]:
let mut buffer = String::new();
let stdin = std::io::stdin();
let r = stdin.read_line(&mut buffer);
!pwd
 ^^^ not found in this scope
cannot find value `pwd` in this scope

Read/Write CSV in Rust

Please refer to Read and Write CSV Files in Rust for details.

Bundle File into Rust Application

Please refer to Bundle Resource Files into a Rust Application for more discussions.

In [20]:
fs::canonicalize("./o1.txt")
Out[20]:
Ok("/workdir/archives/blog/misc/content/2020/11/rust-io/o1.txt")
In [21]:
fs::copy("o1.txt", "out1.txt")
Out[21]:
Ok(30)
In [22]:
fs::create_dir("my_dir")
Out[22]:
Ok(())
In [23]:
fs::create_dir_all("another/dir")
Out[23]:
Ok(())
In [24]:
fs::metadata("o1.txt")
Out[24]:
Ok(Metadata { file_type: FileType(FileType { mode: 33188 }), is_dir: false, is_file: true, permissions: Permissions(FilePermissions { mode: 33188 }), modified: Ok(SystemTime { tv_sec: 1627506651, tv_nsec: 611396728 }), accessed: Ok(SystemTime { tv_sec: 1627506651, tv_nsec: 987468906 }), created: Err(Error { kind: Other, message: "creation time is not available for the filesystem" }), .. })
In [25]:
fs::read_dir(".")
Out[25]:
Ok(ReadDir("."))
In [27]:
for file in fs::read_dir(".").unwrap() {
    println!("{:?}", file);
}
Ok(DirEntry("./.ipynb_checkpoints"))
Ok(DirEntry("./another"))
Ok(DirEntry("./my_dir"))
Ok(DirEntry("./o1.txt"))
Ok(DirEntry("./o2.txt"))
Ok(DirEntry("./o3.txt"))
Ok(DirEntry("./out1.txt"))
Ok(DirEntry("./output.txt"))
Ok(DirEntry("./rust-io.ipynb"))
Out[27]:
()
In [29]:
fs::read_link("out2.txt")
Out[29]:
Ok("o2.txt")

fs::remove_dir

Removes an empty directory.

In [30]:
fs::remove_dir("another")
Out[30]:
Err(Os { code: 39, kind: Other, message: "Directory not empty" })
In [31]:
fs::remove_dir("my_dir")
Out[31]:
Ok(())
In [32]:
fs::remove_dir_all("another")
Out[32]:
Ok(())
In [ ]:
https://doc.rust-lang.org/std/fs/fn.remove_file.html
In [ ]:
https://doc.rust-lang.org/std/fs/fn.rename.html
In [ ]:

Comments