I/O
ロック
Rust の print! マクロと println! マクロは、呼び出しごとに stdout をロックします。これらのマクロを繰り返し呼び出す場合は、stdout を手動でロックした方がよいことがあります。
たとえば、次のコードを変更します:
#![allow(unused)] fn main() { let lines = vec!["one", "two", "three"]; for line in lines { println!("{}", line); } }
次のようにします:
#![allow(unused)] fn main() { fn blah() -> Result<(), std::io::Error> { let lines = vec!["one", "two", "three"]; use std::io::Write; let mut stdout = std::io::stdout(); let mut lock = stdout.lock(); for line in lines { writeln!(lock, "{}", line)?; } // `lock` がドロップされると stdout のロックが解除される Ok(()) } }
stdin と stderr も同様に、それらに対して繰り返し操作を行う場合はロックできます。
バッファリング
Rust のファイル I/O はデフォルトではバッファリングされません。ファイルやネットワークソケットに対して、小さな読み取りまたは書き込み呼び出しを何度も繰り返す場合は、BufReader または BufWriter を使用してください。これらは入力と出力用にメモリ内バッファを保持し、必要なシステムコールの回数を最小限に抑えます。
たとえば、次のバッファリングされていない writer コードを変更します:
#![allow(unused)] fn main() { fn blah() -> Result<(), std::io::Error> { let lines = vec!["one", "two", "three"]; use std::io::Write; let mut out = std::fs::File::create("test.txt")?; for line in lines { writeln!(out, "{}", line)?; } Ok(()) } }
次のようにします:
#![allow(unused)] fn main() { fn blah() -> Result<(), std::io::Error> { let lines = vec!["one", "two", "three"]; use std::io::{BufWriter, Write}; let mut out = BufWriter::new(std::fs::File::create("test.txt")?); for line in lines { writeln!(out, "{}", line)?; } out.flush()?; Ok(()) } }
明示的な flush の呼び出しは厳密には必要ありません。out がドロップされると、自動的に flush が行われるためです。しかし、その場合 flush 時に発生したエラーは無視されます。一方、明示的に flush すれば、そのエラーが明示的になります。
バッファリングを忘れることは、書き込み時によく起こります。バッファリングされていない writer とバッファリングされた writer はどちらも Write トレイトを実装しているため、バッファリングされていない writer へ書き込むコードとバッファリングされた writer へ書き込むコードはほとんど同じです。対照的に、バッファリングされていない reader は Read トレイトを実装していますが、バッファリングされた reader は BufRead トレイトを実装しています。つまり、バッファリングされていない reader から読み取るコードとバッファリングされた reader から読み取るコードは異なります。たとえば、バッファリングされていない reader でファイルを 1 行ずつ読み取るのは難しいですが、バッファリングされた reader では BufRead::read_line または BufRead::lines を使用することで簡単にできます。このため、writer について上で示したような、変更前と変更後が非常によく似ている reader の例を書くのは困難です。
最後に、バッファリングは stdout でも機能することに注意してください。そのため、stdout に対して多数の書き込みを行う場合は、手動ロックとバッファリングを組み合わせるとよいかもしれません。
ファイルから行を読み取る
このセクションでは、BufRead を使用してファイルを 1 行ずつ読み取る際に、過剰なアロケーションを避ける方法を説明します。
入力を生バイトとして読み取る
組み込みの String 型は内部的に UTF-8 を使用しているため、入力を読み込む際に UTF-8 検証による小さいながらもゼロではないオーバーヘッドが発生します。UTF-8 を気にせず入力バイトを処理したいだけの場合(たとえば ASCII テキストを扱う場合)は、BufRead::read_until を使用できます。
バイト指向のデータ行を読み取るための専用クレートや、バイト文字列を扱うための専用クレートもあります。