- バックエンド / リーダー候補
- PdM
- Webエンジニア(シニア)
- 他19件の職種
- 開発
- ビジネス
Goの入出力コードをレビューしていて気付いたのですが、世の中には部分読み取り・部分書き込みを許す(ような標準ライブラリを持つ)言語とそうではない言語があるようです。そこでどのような言語が部分読み取り・部分書き込みを許しているか軽く調査しました。
部分読み取り・部分書き込みとは
「最大1024バイトの読み取り」を要求したときに、入力の終端でもエラーでもないのに20バイトしか返ってこなかった、というような状況を部分読み取り (partial read) といいます。
「456バイトの書き込み」を要求したときに、エラーでないにもかかわらず300バイトしか書き込まれなかった、というような状況を部分書き込み (partial write) といいます。
入出力APIによって、部分読み取り・部分書き込みを許すものとそうでないものがあるようです。 (利用者にとっては、部分読み取り・部分書き込みが許されないほうが話が簡単です)
C: read(2) / write(2)
部分読み取り・部分書き込みがありえる
It is not an error if this number is smaller than the number of bytes requested;
https://man7.org/linux/man-pages/man2/read.2.html
Note that a successful `write()` may transfer fewer than count bytes.
https://man7.org/linux/man-pages/man2/write.2.html
Open Groupではread(2)の部分読み取りはありえる一方、write(2)の部分書き込みはFIFOや通常ファイルに関しては起きないことになっている: https://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html https://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html
C: fread(3) / fwrite(3)
完全読み取り・完全書き込みのみ
Upon successful completion, `fread()` shall return the number of elements successfully read which is less than nitems only if a read error or end-of-file is encountered.
https://man7.org/linux/man-pages/man3/fread.3p.html
The `fwrite()` function shall return the number of elements successfully written, which may be less than nitems if a write error is encountered.
https://man7.org/linux/man-pages/man3/fwrite.3p.html
Rust: Read::read / Write::write
部分読み取り・部分書き込みがありえる
It is not an error if the returned value `n` is smaller than the buffer size, even when the reader is not at the end of the stream yet.
https://doc.rust-lang.org/stable/std/io/trait.Read.html#tymethod.read
// Writes some prefix of the byte string, not necessarily all of it.
buffer.write(b"some bytes")?;
https://doc.rust-lang.org/stable/std/io/trait.Write.html#tymethod.write
完全読み取り・完全書き込みをするための read_exact
/ write_all
ユーティリティーが別途提供されています。
Go: io.Reader / io.Writer
- 部分読み取りがありえる
- 完全書き込みのみ
If some data is available but not len(p) bytes, Read conventionally returns what is available instead of waiting for more.
https://golang.org/pkg/io/#Reader
Write must return a non-nil error if it returns n < len(p).
https://golang.org/pkg/io/#Writer
Java: InputStream / OutputStream
完全読み取り・完全書き込みのみ
This method blocks until input data is available, end of file is detected, or an exception is thrown.
書き込みは戻り値自体がない
public void write(byte[] b, int off, int len) throws IOException
まとめと考察
- Linuxの read(2), write(2) は部分読み取り・部分書き込みを許す (Open Groupの write(2) は部分書き込みに制約がある)
- RustのRead / Writeは部分読み取り・部分書き込みを許す
- Goのio.Readerは部分読み取りを許すが、io.Writerは完全書き込みのみ
これには以下のような事情があるのではないかと思います。
- read(2) / write(2) はI/O多重化と組み合わせることがあるため、ブロッキングのない範囲内でのI/O操作をサポートしたい。
- Rustはシステムプログラミング言語として、上記のような低層APIをそのまま扱えるようにしたい。
- Goはそれ自体が非同期ランタイムを持つため、プログラマーに提供するAPIはI/O多重化を想定する必要がない。
- 入力ストリームはユーザー起因でブロックすることがあるため、きりのよい所まで読み取れるほうが望ましい場合がある。そのため、Goなどでは部分読み取りのみ許している。