]> git.lizzy.rs Git - rust.git/commitdiff
BufWriter: Provide into_raw_parts
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Fri, 4 Dec 2020 17:54:17 +0000 (17:54 +0000)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Fri, 4 Dec 2020 18:28:02 +0000 (18:28 +0000)
If something goes wrong, one might want to unpeel the layers of nested
Writers to perform recovery actions on the underlying writer, or reuse
its resources.

`into_inner` can be used for this when the inner writer is still
working.  But when the inner writer is broken, and returning errors,
`into_inner` simply gives you the error from flush, and the same
`Bufwriter` back again.

Here I provide the necessary function, which I have chosen to call
`into_raw_parts`.

I had to do something with `panicked`.  Returning it to the caller as
a boolean seemed rather bare.  Throwing the buffered data away in this
situation also seems unfriendly: maybe the programmer knows something
about the underlying writer and can recover somehow.

So I went for a custom Error.  This may be overkill, but it does have
the nice property that a caller who actually wants to look at the
buffered data, rather than simply extracting the inner writer, will be
told by the type system if they forget to handle the panicked case.

If a caller doesn't need the buffer, it can just be discarded.  That
WriterPanicked is a newtype around Vec<u8> means that hopefully the
layouts of the Ok and Err variants can be very similar, with just a
boolean discriminant.  So this custom error type should compile down
to nearly no code.

Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
library/std/src/io/buffered/bufwriter.rs

index 067ed6ba7ff50fb972feab5e458e637e6752599a..a1c5f22d41794da38f121d080aee8dd3bb878c98 100644 (file)
@@ -1,7 +1,9 @@
+use crate::error;
 use crate::fmt;
 use crate::io::{
     self, Error, ErrorKind, IntoInnerError, IoSlice, Seek, SeekFrom, Write, DEFAULT_BUF_SIZE,
 };
+use crate::mem;
 
 /// Wraps a writer and buffers its output.
 ///
@@ -287,6 +289,77 @@ pub fn into_inner(mut self) -> Result<W, IntoInnerError<BufWriter<W>>> {
             Ok(()) => Ok(self.inner.take().unwrap()),
         }
     }
+
+    /// Disassembles this `BufWriter<W>`, returning the underlying writer, and any buffered but
+    /// unwritten data.
+    ///
+    /// If the underlying writer panicked, it is not known what portion of the data was written.
+    /// In this case, we return `WriterPanicked` for the buffered data (from which the buffer
+    /// contents can still be recovered).
+    ///
+    /// `into_raw_parts` makes no attempt to flush data and cannot fail.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(bufwriter_into_raw_parts)]
+    /// use std::io::{BufWriter, Write};
+    ///
+    /// let mut buffer = [0u8; 10];
+    /// let mut stream = BufWriter::new(buffer.as_mut());
+    /// write!(stream, "too much data").unwrap();
+    /// stream.flush().expect_err("it doesn't fit");
+    /// let (recovered_writer, buffered_data) = stream.into_raw_parts();
+    /// assert_eq!(recovered_writer.len(), 0);
+    /// assert_eq!(&buffered_data.unwrap(), b"ata");
+    /// ```
+    #[unstable(feature = "bufwriter_into_raw_parts", issue = "none")]
+    pub fn into_raw_parts(mut self) -> (W, Result<Vec<u8>, WriterPanicked>) {
+        let buf = mem::take(&mut self.buf);
+        let buf = if !self.panicked { Ok(buf) } else { Err(WriterPanicked { buf }) };
+        (self.inner.take().unwrap(), buf)
+    }
+}
+
+#[unstable(feature = "bufwriter_into_raw_parts", issue = "none")]
+/// Error returned for the buffered data from `BufWriter::into_raw_parts`, when the underlying
+/// writer has previously panicked.  Contains the (possibly partly written) buffered data.
+pub struct WriterPanicked {
+    buf: Vec<u8>,
+}
+
+impl WriterPanicked {
+    /// Returns the perhaps-unwritten data.  Some of this data may have been written by the
+    /// panicking call(s) to the underlying writer, so simply writing it again is not a good idea.
+    #[unstable(feature = "bufwriter_into_raw_parts", issue = "none")]
+    pub fn into_inner(self) -> Vec<u8> {
+        self.buf
+    }
+
+    const DESCRIPTION: &'static str =
+        "BufWriter inner writer panicked, what data remains unwritten is not known";
+}
+
+#[unstable(feature = "bufwriter_into_raw_parts", issue = "none")]
+impl error::Error for WriterPanicked {
+    #[allow(deprecated, deprecated_in_future)]
+    fn description(&self) -> &str {
+        Self::DESCRIPTION
+    }
+}
+
+#[unstable(feature = "bufwriter_into_raw_parts", issue = "none")]
+impl fmt::Display for WriterPanicked {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{}", Self::DESCRIPTION)
+    }
+}
+
+#[unstable(feature = "bufwriter_into_raw_parts", issue = "none")]
+impl fmt::Debug for WriterPanicked {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "WriterPanicked{{..buf.len={}..}}", self.buf.len())
+    }
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]