Description
BufWriter.drop
is broken.
BufWriter
flushes data on drop
and ignores the result. It is incorrect for two reasons:
- you must not ignore write errors. For example, when filesystem is full, write fails. Currently last write error is quietly ignored. This code demonstrates the problem: http://is.gd/ZdgbF9
drop
may indefinitly hang, for example, ifBufWrtiter
underlying stream is socket, and nobody reads on the other side
Generally, I think any drop
must only release resources, do not do anything blocking or failing.
The similar issue was only partially fixed in #30888.
We (together with @cfallin) propose a solution:
Proposed solution
Add another function to BufWriter
(and probably to Write
trait): cancel
. User of BufWriter
must explicitly call either flush
or cancel
prior to drop.
struct BufWriter
gets unfinished
flag instead of panicked
.
BufWriter.write
unconditionally sets unfinished = true
.
BufWriter.flush
or BufWriter.cancel
unconditionally set unfinished = false
.
And finally, BufWriter.drop
becomes an assertion:
impl Drop for BufWriter {
fn drop(&mut self) {
// check `flush` or `cancel` is explicitly called
// but it is OK to discard buffer on panic
assert!(!self.unfinished || std::thread::panicking());
}
}
That change is backward incompatible, however, it is not that bad: developer will likely get panic on first program run, and can quickly fix the code.
Change could be transitional: no-op cancel
function could be added in rust version current+1 and assertion added in rust version current+2.