Rust勉強中 - その24 -> I/O

# 自己紹介
出田 守と申します。
しがないPythonプログラマです。
情報セキュリティに興味があり現在勉強中です。CTFやバグバウンティなどで腕を磨いています。主に低レイヤの技術が好きで、そっちばかり目が行きがちです。

Rustを勉強していくうえで、読んで学び、手を動かし、記録し、楽しく学んでいけたらと思います。

# 環境
新しい言語を学ぶということで、普段使わないWindowsとVimという新しい開発環境で行っています。
OS: Windows10 Home 64bit 1903
CPU: Intel Core i5-3470 @ 3.20GHz
Rust: 1.38.0
RAM: 8.00GB
Editor: Vim 8.1.1
Terminal: PowerShell

# 前回
前回はテキスト処理と正規表現について学びました。
[Rust勉強中 - その23](https://qiita.com/deta-mamoru/items/48ade3c3e75b945e4f21)

# I/O
std::ioには、RustのI/O操作に必要なRead, BufRead, Write, Seekトレイトや関数がまとめられています。
あらゆるI/Oに関する関数やメソッドの引数にreaderやwriterを渡すものがあります。

* readerはバイト列を読み出すことができる値です。
* writerはバイト列を書き出すことができる値です。

この値にはVec<u8>やファイルストリーム、ネットワークストリームなどがあります。

## Readトレイト
Readトレイトは以下のように定義されています。

```Rust
pub trait Read {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize>;

    fn read_vectored(&mut self, bufs: &mut [IoSliceMut]) -> Result<usize> { ... }
    unsafe fn initializer(&self) -> Initializer { ... }
    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> { ... }
    fn read_to_string(&mut self, buf: &mut String) -> Result<usize> { ... }
    fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> { ... }
    fn by_ref(&mut self) -> &mut Self
    where
        Self: Sized,
    { ... }
    fn bytes(self) -> Bytes<Self>
    where
        Self: Sized,
    { ... }
    fn chain<R: Read>(self, next: R) -> Chain<Self, R>
    where
        Self: Sized,
    { ... }
    fn take(self, limit: u64) -> Take<Self>
    where
        Self: Sized,
    { ... }
}
```
readメソッドは指定されたバッファのデータソースからbyte単位で読み込みます。データがあってもbufferの長さまでしか読み込みません。読み込み時何らかのエラーが発生した場合、io::Errorを返します。さらにio::Errorはkindメソッドを使うことで、ErrorKind列挙型の値を返すことができます。ErrorKind::Interrupted値は、特にこれについて何かすることがなければ処理を再試行しても良いようです。
readメソッドの他に、バイト列を全て読み出すread_to_endメソッド、指定したバイト長を読み出すtakeメソッド、バイトのイテレータを返すbytesメソッドなどがあります。

参照:
[ドキュメント - Read](https://doc.rust-lang.org/std/io/trait.Read.htm)

## BufReadトレイト
```Rust
pub trait BufRead: Read {
    fn fill_buf(&mut self) -> Result<&[u8]>;
    fn consume(&mut self, amt: usize);

    fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> Result<usize> { ... }
    fn read_line(&mut self, buf: &mut String) -> Result<usize> { ... }
    fn split(self, byte: u8) -> Split<Self>
    where
        Self: Sized,
    { ... }
    fn lines(self) -> Lines<Self>
    where
        Self: Sized,
    { ... }
}
```
BufReadトレイトは、readerがバッファリングを利用してシステムコール呼び出しの回数を減らすことでオーバヘッドを減らすことができます。
BufReadトレイトが実装するメソッドに加え、Readトレイトを継承しているため、Readトレイトが実装するメソッドを使用することができます。
fill_bufメソッドはバイト列を読み出します。ただし、呼び出し後にreadメソッドを呼び出すと同じバイト列を読み出す可能性があるのでconsumeメソッドを使ってバイトを消費する必要があります。
read_lineやlinesは行単位で読み出すことができるメソッドです。read_lineは**改行を含む行**をバイト列で読み出し、linesは**改行を除く行**のバイト列のイテレータを返します。
以下はドキュメントの例にあったfill_bufとconsumeの使用例と、read_lineの例です。

```Rust
use std::io;
use std::io::prelude::*;

fn input_line() -> io::Result<String> {
    let stdin    = io::stdin();
    let mut line = String::new();
    stdin.lock().read_line(&mut line)?;
    Ok(line)
}

fn main() {
    {
        // fill_buf, consume
        let stdin = io::stdin();
        let mut stdin = stdin.lock();
        let buffer = stdin.fill_buf().unwrap();
        println!("{:?}", buffer);
        let length = buffer.len();
        stdin.consume(length);
    }
    // read_line
    println!("{:?}", input_line());
}
```
```
aaa
[97, 97, 97, 13, 10]
aaa
Ok("aaa\r\n")
```

linesメソッドなどはResultを返すためエラー処理をする必要があります。そのためcollectメソッドなどのアダプタメソッドを使うときは以下のようにすると良いそうです。

```Rust
fn main() {
    ...
    // collect
    use std::fs::File;
    let fd = File::open("test.txt").unwrap();
    let reader = io::BufReader::new(fd);
    // reader.lines().collect(); // Error
    let _lines: Vec<String> = reader.lines().collect::<io::Result<Vec<String>>>().unwrap(); // ok
}
```
これはResultのFromIterator実装のおかげでこのように書けるようです。以下がその実装です。

```Rust
impl<A, E, V: FromIterator<A>> FromIterator<Result<A, E>> for Result<V, E> {
    ...
}
```
コレクション型をもつResult型はその値でのイテレータを作り出すことができるという実装です。(たぶん)

参照:
[ドキュメント - BufRead](https://doc.rust-lang.org/std/io/trait.BufRead.html)

## Writeトレイト
```Rust
pub trait Write {
    fn write(&mut self, buf: &[u8]) -> Result<usize>;
    fn flush(&mut self) -> Result<()>;

    fn write_vectored(&mut self, bufs: &[IoSlice]) -> Result<usize> { ... }
    fn write_all(&mut self, buf: &[u8]) -> Result<()> { ... }
    fn write_fmt(&mut self, fmt: Arguments) -> Result<()> { ... }
    fn by_ref(&mut self) -> &mut Self
    where
        Self: Sized,
    { ... }
}
```
Writeトレイトは書き出し用のトレイトです。
writeメソッドはバッファのバイト列の一部をwriterに書き出します。flushメソッドはバッファされているデータをwriterに書き出します。
BufWriterのnewを使えば任意のwriterをバッファリングできます。BufWriterをドロップする前にはflushしておくと良いそうです。

参照:
[ドキュメント - Write](https://doc.rust-lang.org/std/io/trait.Write.html)

## std::fs
### File::open
openメソッドは、読込み専用としてファイルをオープンします。

```Rust
fn main() {
    ...
    // open
    let fd = File::open("test.txt").unwrap();
}
```

### File::create
createメソッドは、書出し専用としてファイルを新しくオープンします。もし、既にファイルが存在する場合、上書きします。

```Rust
fn main() {
    ...
    // create
    let fd = File::create("test2.txt").unwrap();
}
```

### OpenOptions
OpenOptions型のメソッドを利用することで、ファイルオープン時のフラグを細かく設定できます。
例えば、以下はファイルを追記でオープンする例です。

```Rust
fn main() {
    ...
    // OpenOptions append
    use std::fs::OpenOptions;
    let fd = OpenOptions::new()
        .append(true)
        .open("test.txt").unwrap();
}
```

参照:
[ドキュメント - OpenOptions](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html)

今回はここまで！