Skip to content

Commit ddf9eff

Browse files
committed
Merge pull request rust-lang#1 from rkjnsn/patch-1
Modify read_full/read_exact RFC
2 parents d5284eb + f65d966 commit ddf9eff

File tree

1 file changed

+76
-43
lines changed

1 file changed

+76
-43
lines changed

text/0000-read-all.md

+76-43
Original file line numberDiff line numberDiff line change
@@ -5,62 +5,95 @@
55

66
# Summary
77

8-
Rust's Write trait has write_all, which attempts to write an entire
9-
buffer. This proposal adds two new methods, read_full and read_exact.
10-
read_full attempts to read a fixed number of bytes into a given
11-
buffer, and returns Ok(n) if it succeeds or in the event of EOF.
12-
read_exact attempts to read a fixed number of bytes into a given
13-
buffer, and returns Ok(n) if it succeeds and Err(ErrorKind::ShortRead)
14-
if it fails.
8+
Rust's `Write` trait has `write_all`, which is a convenience method that calls
9+
`write` repeatedly to write an entire buffer. This proposal adds two similar
10+
convenience methods to the `Read` trait: `read_full` and `read_exact`.
11+
`read_full` calls `read` repeatedly until the buffer has been filled, EOF has
12+
been reached, or an error other than `Interrupted` occurs. `read_exact` is
13+
similar to `read_full`, except that reaching EOF before filling the buffer is
14+
considered an error.
1515

1616
# Motivation
1717

18-
The new read_exact method will allow programs to read from disk
19-
without having to write their own read loops to handle EINTR. Most
20-
Rust programs which need to read from disk will prefer this to the
21-
plain read function. Many C programs have the same need, and solve it
22-
the same way (e.g. git has read_in_full). Here's one example of a
23-
Rust library doing this:
24-
https://github.com/BurntSushi/byteorder/blob/master/src/new.rs#L184
25-
26-
The read_full method is useful the common case of implementing
27-
buffered reads from a file or socket. In this case, a short read due
28-
to EOF is an expected outcome, and the caller must check the number of
29-
bytes returned.
18+
The `read` method may return fewer bytes than requested, and may fail with an
19+
`Interrupted` error if a signal is received during the call. This requires
20+
programs wishing to fill a buffer to call `read` repeatedly in a loop. This is
21+
a very common need, and it would be nice if this functionality were provided in
22+
the standard library. Many C and Rust programs have the same need, and solve it
23+
in the same way. For example, Git has [`read_in_full`][git], which behaves like
24+
the proposed `read_full`, and the Rust byteorder crate has
25+
[`read_full`][byteorder], which behaves like the proposed `read_exact`.
26+
[git]: https://github.com/git/git/blob/16da57c7c6c1fe92b32645202dd19657a89dd67d/wrapper.c#L246
27+
[byteorder]: https://github.com/BurntSushi/byteorder/blob/2358ace61332e59f596c9006e1344c97295fdf72/src/new.rs#L184
3028

3129
# Detailed design
3230

33-
The read_full function will take a mutable, borrowed slice of u8 to
34-
read into, and will attempt to fill that entire slice with data.
31+
The following methods will be added to the `Read` trait:
32+
33+
``` rust
34+
fn read_full(&mut self, buf: &mut [u8]) -> Result<usize>;
35+
fn read_exact(&mut self, buf: &mut [u8]) -> Result<()>;
36+
```
37+
38+
Additionally, default implementations of these methods will be provided:
39+
40+
``` rust
41+
fn read_full(&mut self, mut buf: &mut [u8]) -> Result<usize> {
42+
let mut read = 0;
43+
while buf.len() > 0 {
44+
match self.read(buf) {
45+
Ok(0) => break,
46+
Ok(n) => { read += n; let tmp = buf; buf = &mut tmp[n..]; }
47+
Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
48+
Err(e) => return Err(e),
49+
}
50+
}
51+
Ok(read)
52+
}
3553

36-
It will loop, calling read() once per iteration and attempting to read
37-
the remaining amount of data. If read returns EINTR, the loop will
38-
retry. If there are no more bytes to read (as signalled by a return
39-
of Ok(0) from read()), the number of bytes read so far
40-
will be returned. In the event of another error, that error will be
41-
returned. After a read call returns having successfully read some
42-
bytes, the total number of bytes read will be updated. If that total
43-
is equal to the size of the buffer, read_full will return successfully.
54+
fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
55+
if try!(self.read_full(buf)) != buf.len() {
56+
Err(Error::new(ErrorKind::UnexpectedEOF, "failed to fill whole buffer"))
57+
} else {
58+
Ok(())
59+
}
60+
}
61+
```
4462

45-
The read_exact method can be implemented in terms of read_full.
63+
Finally, a new `ErrorKind::UnexpectedEOF` will be introduced, which will be
64+
returned by `read_exact` in the event of a premature EOF.
4665

4766
# Drawbacks
4867

49-
The major weakness of this API (shared with write_all) is that in the
50-
event of an error, there is no way to return the number of bytes that
51-
were successfully read before the error. But returning that data
52-
would require a much more complicated return type, as well as
53-
requiring more work on the part of callers.
68+
Like `write_all`, these APIs are lossy: in the event of an error, there is no
69+
way to determine the number of bytes that were successfully read before the
70+
error. However, doing so would complicate the methods, and the caller will want
71+
to simply fail if an error occurs the vast majority of the time. Situations
72+
that require lower level control can still use `read` directly.
73+
74+
# Unanswered Questions
75+
76+
Naming. Is `read_full` the best name? Should `UnexpectedEOF` instead be
77+
`ShortRead` or `ReadZero`?
5478

5579
# Alternatives
5680

57-
One alternative design would return some new kind of Result which
58-
could report the number of bytes sucessfully read before an error.
81+
Use a more complicated return type to allow callers to retrieve the number of
82+
bytes successfully read before an error occurred. As explained above, this
83+
would complicate the use of these methods for very little gain. It's worth
84+
noting that git's `read_in_full` is similarly lossy, and just returns an error
85+
even if some bytes have been read.
86+
87+
Only provide `read_exact`, but parameterize the `UnexpectedEOF` or `ShortRead`
88+
error kind with the number of bytes read to allow it to be used in place of
89+
`read_full`. This would be less convenient to use in cases where EOF is not an
90+
error.
5991

60-
If we wanted one method instead of two, ErrorKind::ShortRead could be
61-
parameterized with the number of bytes read before EOF. But this
62-
would increase the size of ErrorKind.
92+
Only provide `read_full`. This would cover most of the convenience (callers
93+
could avoid the read loop), but callers requiring a filled buffer would have to
94+
manually check if all of the desired bytes were read.
6395

64-
Or we could leave this out, and let every Rust user write their own
65-
read_full or read_exact function, or import a crate of stuff just for
66-
this one function.
96+
Finally, we could leave this out, and let every Rust user needing this
97+
functionality continue to write their own `read_full` or `read_exact` function,
98+
or have to track down an external crate just for one straightforward and
99+
commonly used convenience method.

0 commit comments

Comments
 (0)