In Rust, file I/O is handled primarily through the std::fs
and std::io
modules. The std::fs
module contains several functions for manipulating the filesystem, such as creating, removing, and reading files and directories. The std::io
module contains traits, structs, and enums that can be used to handle input/output in a more abstract way, and is also used for error handling.
Here are a few examples of file operations in Rust:
1. Reading a File
use std::fs::File;
use std::io::Read;
fn main() -> std::io::Result<()> {
let mut file = File::open("foo.txt")?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
println!("{}", contents);
Ok(())
}
This program opens the file foo.txt
, reads its contents into a string, and then prints the string.
Note: The ?
operator in Rust is used for error handling. It’s a shorthand way to propagate errors up the call stack.
When you call a function that returns a Result
type, it will return either an Ok(T)
variant which contains the successful result, or an Err(E)
variant which contains the error information.
If you use the ?
operator on a Result
value, it has the effect of “unwrapping” the Result
if it’s the Ok(T)
variant and returning the contained value. However, if the Result
is an Err(E)
variant, the function will immediately return this Err
from the current function.
In the line let mut file = File::open("foo.txt")?;
, File::open("foo.txt")
returns a Result
. If the file is opened successfully, ?
unwraps the Result
and file
gets the File
object. If there’s an error (e.g., the file does not exist, or the program doesn’t have permission to access it), the ?
operator returns early from the function and gives the error.
The ?
operator can only be used in functions that return a Result
(or Option
), because when an error occurs, ?
returns it (it must return the same type as the function). So in the main function, you should specify that it returns a Result
. If an error bubbles up to the main
function, the error information will be printed to the standard error stream and the program will exit.
fn main() -> std::io::Result<()> {
let mut file = File::open("foo.txt")?;
// ...
Ok(())
}
2. Writing to a File
use std::fs::File;
use std::io::Write;
fn main() -> std::io::Result<()> {
let mut file = File::create("foo.txt")?;
file.write_all(b"Hello, world!")?;
Ok(())
}
This program creates a file named foo.txt
, writes the byte string Hello, world!
into the file, and then closes the file.
3. Working with Directories
use std::fs;
fn main() -> std::io::Result<()> {
fs::create_dir("foo_dir")?; // create a directory
fs::remove_dir("foo_dir")?; // remove the directory
Ok(())
}
This program creates a directory named foo_dir
, then removes it.
All of the functions used in these examples (File::open
, File::create
, fs::create_dir
, etc.) can fail, for example, due to permissions, missing files, etc. They return a Result
type, and by returning std::io::Result<()>
from main()
, these errors will automatically be handled by Rust: it will stop the program and print an error message.
Additionally, Rust has support for reading from and writing to files in a line-by-line or byte-by-byte manner, and includes many other features for advanced file I/O handling. These include file metadata, permissions, and more complex read/write operations.
A comprehensive case is as follows:
use std::fs::{self, OpenOptions};
use std::io::{Write, Read};
fn main() {
let file = std::fs::File::open("data.txt");
println!("文件打开\n{:?}", file);
let file = std::fs::File::create("data2.txt").expect("创建失败");
println!("文件创建成功{:?}", file);
fs::remove_file("data.txt").expect("无法删除文件");
println!("文件已删除");
let mut file = OpenOptions::new().append(true).open("data2.txt").expect("失败");
// file.write("\nRust Programming Language".as_bytes()).expect("写入失败");
// println!("\n数据追加成功");
file.write_all("Rust".as_bytes()).expect("失败");
file.write_all("\nRust".as_bytes()).expect("失败");
println!("\n数据写入成功");
// // write_all并不会在写入后自动写入换行\n
let mut file = std::fs::File::open("data2.txt").unwrap();
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();
println!("{}", contents);
}