Rust's standard library covers the basic file operations you need for small tools: reading a file line by line, opening a file for writes, listing a directory, and copying files. The examples keep error handling close to the calls so both results and failures stay visible.

Reading a file line by line

BufReader wraps a File and gives you an iterator over lines. Each line is printed with debug formatting, which keeps quotes and whitespace visible.

use std::io::{self, BufReader};
use std::io::prelude::*;
use std::fs::File;

fn readline_and_print() -> io::Result<()> {
    let f = File::open("foo.txt")?;
    let f = BufReader::new(f);

    for line in f.lines() {
        if let Ok(line) = line {
            println!("{:?}", line);
        }
    }
    Ok(())
}

fn main() {
    match readline_and_print() {
        Ok(()) => println!("it's ok"),
        Err(err) => println!("zzz: {}", err)
    }
}

The wrapper function returns io::Result<()>, so main can match on the result and print either success or the error.

Opening a file for writing

OpenOptions lets you combine access modes. In the example, read(true) and write(true) allow both directions, create(true) opens an existing file or creates it when missing, append(true) sends writes to the end, and the commented create_new(true) would instead fail if the file already exists. That failure is reported as an OS error with code 17 and the message File exists. When the open succeeds, the handle is stored in stream and write_all writes the whole byte string.

use std::io;
use std::io::prelude::*;
use std::fs::OpenOptions;

fn run() -> io::Result<()> {
    let filename = "foo.txt";
    let file = OpenOptions::new()
                .read(true)
                .write(true)
                .create(true)
                //.create_new(true)
                .append(true)
                .open(filename);

    match file {
        Ok(mut stream) => {
            stream.write_all(b"hello, world!\n")?;
        }
        Err(err) => {
            println!("{:?}", err);
        }
    }
    Ok(())
}

fn main() {
    match run() {
        Ok(()) => println!("It's ok"),
        Err(err) => println!("zzz: {}", err)
    }
}

Listing a directory

fs::read_dir returns entries for the path passed in. The code loops over the result and prints each entry, then prints the path, file name, and file type for entries that were read successfully.

use std::fs;

fn run() {
    if let Ok(entries) = fs::read_dir("photos") {
        for entry in entries {
            println!("{:?}", entry);
            if let Ok(entry) = entry {
                println!("{:?}", entry.path());
                println!("{:?}", entry.file_name());
                println!("{:?}", entry.file_type());
            }
        }
    }
}

fn main() {
    run();
}

Copying a file with a timestamped name

The copy example builds a destination name under funny/ using a Unix timestamp for uniqueness. It calls time::get_time(), combines seconds and nanoseconds into a floating-point value, turns that into a string, splits at the decimal point, and uses the whole-seconds part. Before copying, it checks whether the funny directory exists and creates it if needed. Then fs::copy copies /root/test.db to the generated path.

use std::fs;
use std::io;
use std::path::Path;
extern crate time;

// copy file to other area

fn timestamp() -> f64 {
    let timespec = time::get_time();
    // 1459440009.113178
    let mills: f64 = timespec.sec as f64 + (timespec.nsec as f64 / 1000.0 / 1000.0 / 1000.0);
    mills
}

fn aha() -> io::Result<()> {

    let ts = timestamp();
    let ts_str = ts.to_string();

    let v: Vec<&str> = ts_str.split(".").collect();
    let filename = "funny/funny_".to_owned() + v[0] + ".db";
    println!("file name: {}", filename);

    if !Path::new("funny").exists() {
        fs::create_dir("funny")?;
    }

    fs::copy("/root/test.db", filename)?;
    Ok(())
}

fn main() {
    match aha() {
        Ok(s) => println!("ok: {:?}", s),
        Err(err) => println!("err: {:?}", err),
    }
}