The File struct from std::fs represents a file that has been opened (it wraps a file descriptor), and gives read and/or write access to the underlying file.
Since many things can go wrong when doing file I/O, explicit and proactive handling of all possible errors is certainly needed. This can be done with pattern matching, because all the File methods return the std::io::Result<T> type, which is an alias for Result<T, io::Error>.
To open a file in read-only mode, use the static File::open method with a reference to its path, and match the file handler or a possible error like this:
// code from Chapter 11/code/read_file.rs: use std::path::Path; use std::fs::File; use std::io::prelude::*; use std::error::Error; fn main() { let path = Path::new("hello.txt"); let display = path.display(); let mut file = match File::open(&path) { Ok(file) => file, Err(why) => panic!("couldn't open {}: {}", display, Error::description(&why)) }; }
In the case of a failing open method (because the file does not exist or the program has no access) this prints out:
thread '<main>' panicked at 'couldn't open hello999.txt: os error', F:\Rust\Rust book\Chapter 11 - Working with files\code\read_file.rs:11
Explicitly closing files is not necessary in Rust; the file handle goes out of scope at the end of main or the surrounding block, causing the file to automatically close.
We can read in the file in a content string by using the read_to_string() method, which again returns a Result value, necessitating a pattern match. In order to be able to use this, we need to do the import use std::io::prelude::*:
let mut content = String::new(); match file.read_to_string(&mut content) { Err(why) => panic!("couldn't read {}: {}", display, Error::description(&why)), Ok(_) => print!("{} contains:\n{}", display, content), }
This prints out:
hello.txt contains:
"Hello Rust World!"
std::io::prelude imports common I/O traits in our code, such as Read, Write, BufRead, and Seek.