【Rust入門】ファイル入出力の基本を分かりやすく解説

Rust のファイル入出力の基本について解説します。
Rust の入出力
プログラミング言語において、ファイル入出力は非常に基本的で重要な処理です。Rust の入出力は、Read
、BufRead
、Write
というトレイトを中心に構築されています。入出力という広い観点で見ると「ファイル」「標準入出力」「ネットワーク」など様々な対象がありますが、Rust では、いずれもこれらのトレイトを通じて扱うことができます。
この記事では、トレイトの細かなところまでは踏み込みませんが、テキストファイルの入出力を通じて Rust でのファイル入出力の基本を紹介します。
テキストファイルの入出力
テキストファイルの読み込み
ファイルを開いて全てのテキストを読み込む read_to_string
テキストファイルを開いて内容を読み込むには、read_to_string
メソッドを使用します。
use std::fs; use std::io::{self}; fn read_file(filepath: &str) -> io::Result<()> { // ファイルパスのデータを String 型で取得する let contents = fs::read_to_string(filepath)?; // 読み込んだ文字列を表示する println!("{}", contents); Ok(()) } fn main() { // ファイルパスは任意のパスに変更してください let filepath = r"D:\RustProject\rust-tech-sample-source\rust-basic\file_input_output\examples\input_example.txt"; // ファイル読み込み関数を呼び出し、エラーが発生した場合はエラーを表示する if let Err(e) = read_file(filepath) { println!("Error: {}", e); } }
【実行結果】※ input_example.txt の内容 Rust Programming ファイル入出力の基本 テキストファイルの入出力
「use std::fs;
」とすることで、標準ライブラリ std
内の fs
モジュールを使用します。read_to_string
は fs
モジュール内の関数で、ファイルパスを渡すことで、ファイルの中身を String
型で読み込むことができます。
この read_to_string
関数の返却値の型は、io::Result<String>
型になります。上記例の read_file
関数では、返却値を io::Result<()>
として、? 演算子によりエラーが発生した場合や呼び出し元に移譲しています。
エラーの場合は、呼び出し元の main
関数内で if let
により Err
を補足してエラーを表示するようにしています。より丁寧にエラー処理をする場合は、match
による処理ももちろん可能です。エラー処理の基本が分からない方は「エラー処理の基本を分かりやすく解説」も参考にしてください。
ファイルを開いてから読み込む
read_to_string
関数は、上記のようにファイルパスを指定して簡単にテキストファイルを読み込むことができますが、以下の例のように fs::File::open
によりファイルを開いてから、読込先の String
を渡して読み込むことも可能です。
use std::fs; use std::io::{self, Read}; fn read_file(filepath: &str) -> io::Result<()> { let mut file = fs::File::open(filepath)?; let mut contents = String::new(); // ファイルの内容を読み込み file.read_to_string(&mut contents)?; // 読み込んだ文字列を表示する println!("{contents}"); Ok(()) }
この時、Read
トレイトを use
指定する必要がある点に注意してください。先ほどまで使用していた fs::read_to_string
は fs
モジュール内に定義された関数でした。一方で、上記例は File
型にトレイトのメソッドとして定義した read_to_string
を使用しているため、似ているように見えて全くの別物です。
トレイトで定義されるメソッドを使用する際には、use
でトレイトを読み込む必要があるため、上記の例では Read
トレイトの読み込みが必要となるわけです。
1行ずつ読み込む
ファイルを 1行ずつ読み込む場合には、BufReader
により順次処理をすることが可能です。
use std::fs::File; use std::io::{self, BufRead, BufReader}; fn read_lines(filepath: &str) -> io::Result<()> { let file = File::open(filepath)?; let reader = BufReader::new(file); // ファイルを1行ずつ読み込む for line in reader.lines() { // line は Result<String, Error> なので値を取り出す let line = line?; println!("{} :文字数({})", line, line.chars().count()); } Ok(()) } fn main() { // ファイルパスは任意のパスに変更してください let filepath = r"D:\RustProject\rust-tech-sample-source\rust-basic\file_input_output\examples\input_example.txt"; // ファイル読み込み関数を呼び出し、エラーが発生した場合はエラーを表示する if let Err(e) = read_lines(filepath) { println!("Error: {}", e); } }
【実行結果】 Rust Programming :文字数(16) ファイル入出力の基本 :文字数(10) テキストファイルの入出力 :文字数(12)
上記例では、まず File::open
によりファイルを開き、BufReader
の new
関数に渡すことで reader
を生成します。reader.lines()
により各行のデータを順に取得することができるため、for
文で line
に値を入れつつ 1行ずつ処理をすることが可能です。
なお、BufRead
トレイトと BufReader
型を use 指定する必要があるので注意してください。
取り出される line
は、Result<String, Error>
型であるため、?
演算子により値を取り出してから使用しています。このようにすることで、1行ずつファイルを読み込み処理をすることができます。
テキストファイルの書き込み
入力は主に read_to_string
などのメソッドを用いて行いましたが、書き込みでは write!
マクロを使用します。
ファイルを作成して書き込む
ファイルの書き込みを行う場合には、OpenOptions
型を使用します。この型では、「.
」でメソッドを連結することでファイルを開く際のオプションを指定することが可能になっています。
use std::fs::OpenOptions; use std::io::{self, Write}; fn write_new_file(filepath: &str, text: &str) -> io::Result<()> { // ファイルを作成して開く let mut file = OpenOptions::new() .write(true) .create_new(true) // ファイルが存在するとエラー .open(filepath)?; // 文字列を書き込む writeln!(file, "{text}")?; Ok(()) } fn main() { // ファイルパスは任意のパスに変更してください let filepath = r"D:\RustProject\rust-tech-sample-source\rust-basic\file_input_output\examples\output_example.txt"; // 書き込み文字列を準備 let write_str = "Rust Programming\nファイル入出力の基本\nテキストファイルの入出力"; // ファイル書き込み関数を呼び出し、エラーが発生した場合はエラーを表示する if let Err(e) = write_new_file(filepath, write_str) { println!("Error: {e}"); } }
ファイルを書き込む際には「.write(true).create_new(true).open(filepath)
」とすることで、指定したファイルパスでファイルを作成して、書き込みモードで開くことができます。この時ファイルが存在する場合は、エラーとなります。
ファイルを書き込む場合には、writeln!
マクロを使用します。このマクロは、println!
マクロとほとんど同じですが、第1引数に File
型のインスタンスを指定していることとが異なります。なお、改行を含まないように出力したい場合は write!
マクロで対応可能です。
また、返却値として Result
型が返ってくる点も println!
マクロとは異なるためエラー処理が必要です。上記例では ?
演算子により処理をしています。
ファイルを開いて追記する
ログファイルの出力など、ファイルを追記モードで開いて末尾に文字列を追記したくなることが良くあります。この場合は、以下のように「.append(true)
」とすることで対応可能です。
use std::fs::OpenOptions; use std::io::{self, Write}; fn write_append_file(filepath: &str, text: &str) -> io::Result<()> { // ファイルを追記モードで開く let mut file = OpenOptions::new() .append(true) // ファイルが存在しない場合、エラー .open(filepath)?; // 文字列を書き込む writeln!(file, "{text}")?; Ok(()) } fn main() { let filepath = r"D:\RustProject\rust-tech-sample-source\rust-basic\file_input_output\examples\output_example.txt"; // 追記する文字列を準備 let append_str = "Rust 文字列の追記"; // ファイル書き込み関数を呼び出し、エラーが発生した場合はエラーを表示する if let Err(e) = write_append_file(filepath, append_str) { println!("Error: {e}"); } }
この場合、ファイルが存在しない場合はエラーとなるので注意してください。
まとめ
Rust のファイル入出力の基本について解説しました。この記事では、テキストファイルの読み込み・書き込みを中心に紹介しています。
ファイル入出力は、どのようなプログラミング言語においても基本になります。Rust の I/O を理解することで、より安全でパフォーマンスの高いプログラムが書けるようになりましょう。