|
5 | 5 |
|
6 | 6 | # Summary
|
7 | 7 |
|
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. |
15 | 15 |
|
16 | 16 | # Motivation
|
17 | 17 |
|
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 |
30 | 28 |
|
31 | 29 | # Detailed design
|
32 | 30 |
|
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 | +} |
35 | 53 |
|
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 | +``` |
44 | 62 |
|
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. |
46 | 65 |
|
47 | 66 | # Drawbacks
|
48 | 67 |
|
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`? |
54 | 78 |
|
55 | 79 | # Alternatives
|
56 | 80 |
|
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. |
59 | 91 |
|
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. |
63 | 95 |
|
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