ファイルの読み出しと書き込み――Rustのファイル入出力を理解する基本からしっかり学ぶRust入門(15)

Rustについて基本からしっかり学んでいく本連載。第15回は、ファイルや標準入出力とのデータのやりとりについて。

» 2022年09月22日 05時00分 公開

この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。

「基礎からしっかり学ぶRust入門」のインデックス

連載:基礎からしっかり学ぶRust入門

 本連載のサンプルコードをGitHubで公開しています。こちらからダウンロードしてみてください。具体的な利用方法は連載第1回を参考にしてください。


ファイル入出力関連モジュール

 Rustのファイル入出力については、第8回でその片りんを紹介しました。コマンドラインで受け取ったファイル名からそのファイルをオープンし、内容を読み込んだ上で表示するというシンプルなものでした。この例で見たように、Rustにおいてファイル入出力を扱うのはとても簡単です。

 ファイル関連の機能を扱うのは、主にstd::fsモジュールとstd::ioモジュールの関数です。この2つのモジュールは、以下のような役割分担となっています。

  • std::fsモジュール……ファイルのオープンや作成、ファイル/ディレクトリの操作
  • std::ioモジュール……入出力関連のトレイト、標準入出力など

 オープンなどファイルそのものの操作はstd::fsモジュールに集約され、オープンした結果のファイルハンドルを用いた入出力はstd::ioモジュールに集約されています。標準入出力など、アプリケーションがファイルのオープンとクローズに関与しない場合でも、std::ioモジュールのメソッドを使って入出力操作が可能です。

 なお、例えばstd::netモジュールのTcpStream構造体によるネットワーク上の入出力も、std::ioモジュールのトレイトを利用します。このようにstd::ioモジュールのトレイトは、ファイル入出力に限定されませんが、今回はファイル入出力に絞って紹介していきます。

 第8回と同様にサンプルのテキストファイルwagahaiwa_nekodearu.txtを用意して、それに対して操作しています。なお、今回のサンプルはiosパッケージとして作成していきます。

ファイルのオープン(作成)とクローズ

 まずは、ファイル入出力の基本とも言える、オープンとクローズを見ていきます。

ファイルのオープンとクローズ

 Rustにおいてファイルはstd::fsモジュールのFile構造体がつかさどります。第8回の例を今回用に少し手を入れて以下に示します。

use std::fs::File;		(1)
use std::io::Result;
use std::io::prelude::*;
fn main() -> Result<()> {				(2)
    let filename = "wagahaiwa_nekodearu.txt";
    let mut file = File::open(filename)?;		(3)
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    println!("{}", contents);
    Ok(())						(4)
}
src/bin/open.rs

 (1)からはじまる3行は、モジュールのインポートです。このサンプルでは、std::fsモジュールのFile構造体、std::ioモジュールのResult列挙体、std::ioモジュール下のトレイトにある入出力関数(この場合はread_to_string)を使うので、このような宣言になります。以降、使用するモジュールや構造体、関数に応じてこの部分は変化していきますので、use文と対応する構造体とメソッドを対比してみてください。

 (2)は、main関数がエラーを委譲されるという意味です。これにより、main関数中のエラーはmain関数を呼び出した側が引き受けます。本来は、個別の関数呼び出しでエラー処理すべきですが、簡略化のためにこの記法を用いています。詳しくは、第8回を参照してください。

 (3)は、ファイルをオープンしてFile構造体のインスタンスを取得しています。File構造体とはファイルハンドルであり、これを用いてstd::ioモジュール内のトレイトに実装される関数で入出力を処理します。

 (4)は、ここまで何の問題もなかったということで、Result列挙体のOk値を返す記述です。このパターンも、main関数の戻り値とともに以降のサンプルで共通です。

 このサンプルにはファイルをクローズする部分がありませんが、File構造体のインスタンスを受けるfile変数がスコープから外れるとき、インスタンスの破棄とともに内部的にdrop関数が呼び出され、ファイルのクローズが実行されます。このため、ファイルクローズの明示的な指示は基本的に不要となっています。

【補足】preludeモジュール

 上記のサンプル(1)で、3番目のuse文に含まれるpreludeモジュールは、利用頻度の高いトレイトの利用をまとめて宣言できる便利なモジュールで、この場合は続く「::*」によってBufRead、Seek、Read、Writeの4つのトレイトすべてを利用可能にします。「*」の部分をReadなどにすれば個別のトレイトのみに限定できますが、それだとpreludeを使う意味はあまりないので、「use std::io::Read;」と記述するようにします。

ファイルの作成

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。