I am trying to implement FastCGI protocol in rust. And when I had a problem I asked a question here on stackoverflow (the question is this). And as you can see the answer to my question says this:
Then use read_exact to read the header of the response, extract its content length and read_exact again to get contents of the response:
And I really had no problem with that. But since then, I've come across people (both online and in real life) who say something pretty similar to this:):
Avoid the "exact" functions as they might silently swallow some stream content, see the docs.
So my question is. Should I use read_exact
for my specific case? Are the people who said not to use read_exact
right? Why should I occupy it or why should I NOT occupy it? If you need more context about my case in particular, everything is in my question that I passed at the beginning of this post
CodePudding user response:
Are you sure the people who discourage using read_exact
are referring to Rust? Upon searching for this, I was only able to find people talking about other languages.
It should be fine to use read_exact
.
To start off, we can see that it will never read more bytes than necessary in the documentation:
If this function returns an error, it is unspecified how many bytes it has read, but it will never read more than would be necessary to completely fill the buffer.
The only information we lose by using read_exact
is the exact number of bytes we read upon reaching an error and any ErrorKind::Interrupted
errors. However, upon reading the documentation for interrupted errors we can see it states as follows.
This operation was interrupted.
Interrupted operations can typically be retried.
Unless you are explicitly using some library which states some additional action is required before continuing to read after an interrupted error, you can assume this action can be retried until either the bytes are read or an error indicating that reading is no longer possible occurs. If you are using read_exact
on a generic std::io::Read
input, it is safe to assume that the caller has handled this use case appropriatly.
You can also verify this is correct by reading the standard library implementation of read_exact
. I have yet to see any major library choose to provide their own implementation for read_exact
, but it is in theory possible. However, they should still adhere to the documentation for read_exact
which yields the same conclusion as before.
pub trait Read {
#[stable(feature = "read_exact", since = "1.6.0")]
fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
default_read_exact(self, buf)
}
// etc...
}
pub(crate) fn default_read_exact<R: Read ?Sized>(this: &mut R, mut buf: &mut [u8]) -> Result<()> {
while !buf.is_empty() {
match this.read(buf) {
Ok(0) => break,
Ok(n) => {
let tmp = buf;
buf = &mut tmp[n..];
}
Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
if !buf.is_empty() {
Err(error::const_io_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer"))
} else {
Ok(())
}
}