From 381763185eaf0940d3b587c5c3ae54cdd4fbe64e Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Fri, 4 Dec 2020 17:54:17 +0000 Subject: [PATCH 1/5] BufWriter: Provide into_raw_parts 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 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 --- library/std/src/io/buffered/bufwriter.rs | 73 ++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index 067ed6ba7ff..a1c5f22d417 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -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 @@ impl BufWriter { Ok(()) => Ok(self.inner.take().unwrap()), } } + + /// Disassembles this `BufWriter`, 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, 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, +} + +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 { + 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")] From 7fab9cb8acdd8f2cab1271c0d7dfb268f2048f23 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Sat, 12 Dec 2020 12:34:48 +0000 Subject: [PATCH 2/5] bufwriter::WriterPanicked: Provide panicking example Signed-off-by: Ian Jackson --- library/std/src/io/buffered/bufwriter.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index a1c5f22d417..94e625b9410 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -324,6 +324,30 @@ impl BufWriter { #[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. +/// +/// # Example +/// +/// ``` +/// #![feature(bufwriter_into_raw_parts)] +/// use std::io::{self, BufWriter, Write}; +/// use std::panic::{catch_unwind, AssertUnwindSafe}; +/// +/// struct PanickingWriter; +/// impl Write for PanickingWriter { +/// fn write(&mut self, buf: &[u8]) -> io::Result { panic!() } +/// fn flush(&mut self) -> io::Result<()> { panic!() } +/// } +/// +/// let mut stream = BufWriter::new(PanickingWriter); +/// write!(stream, "some data").unwrap(); +/// let result = catch_unwind(AssertUnwindSafe(|| { +/// stream.flush().unwrap() +/// })); +/// assert!(result.is_err()); +/// let (recovered_writer, buffered_data) = stream.into_raw_parts(); +/// assert!(matches!(recovered_writer, PanickingWriter)); +/// assert_eq!(buffered_data.unwrap_err().into_inner(), b"some data"); +/// ``` pub struct WriterPanicked { buf: Vec, } From 5ac431fb084e32b8ea86b800c98738ce49ddab37 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Sat, 12 Dec 2020 13:37:29 +0000 Subject: [PATCH 3/5] WriterPanicked: Use debug_struct Co-authored-by: Ivan Tham --- library/std/src/io/buffered/bufwriter.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index 94e625b9410..4319febbf91 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -382,7 +382,9 @@ impl fmt::Display for WriterPanicked { #[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()) + fmt.debug_struct("WriterPanicked") + .field("buffer", &format_args!("{}/{}", self.buf.len(), self.buf.capacity())) + .finish() } } From 79c72f57d5538576cac7df13559ee74dd32552da Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Sat, 12 Dec 2020 18:39:30 +0000 Subject: [PATCH 4/5] fixup! WriterPanicked: Use debug_struct --- library/std/src/io/buffered/bufwriter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index 4319febbf91..145da471596 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -382,7 +382,7 @@ impl fmt::Display for WriterPanicked { #[unstable(feature = "bufwriter_into_raw_parts", issue = "none")] impl fmt::Debug for WriterPanicked { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("WriterPanicked") + f.debug_struct("WriterPanicked") .field("buffer", &format_args!("{}/{}", self.buf.len(), self.buf.capacity())) .finish() } From dea6d6c909ea926d6cb12e3cab98a62d17c32a99 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Mon, 4 Jan 2021 15:35:28 +0000 Subject: [PATCH 5/5] BufWriter::into_raw_parts: Add tracking issue number Signed-off-by: Ian Jackson --- library/std/src/io/buffered/bufwriter.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index 145da471596..8d56e7ae9dc 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -313,7 +313,7 @@ impl BufWriter { /// assert_eq!(recovered_writer.len(), 0); /// assert_eq!(&buffered_data.unwrap(), b"ata"); /// ``` - #[unstable(feature = "bufwriter_into_raw_parts", issue = "none")] + #[unstable(feature = "bufwriter_into_raw_parts", issue = "80690")] pub fn into_raw_parts(mut self) -> (W, Result, WriterPanicked>) { let buf = mem::take(&mut self.buf); let buf = if !self.panicked { Ok(buf) } else { Err(WriterPanicked { buf }) }; @@ -321,7 +321,7 @@ impl BufWriter { } } -#[unstable(feature = "bufwriter_into_raw_parts", issue = "none")] +#[unstable(feature = "bufwriter_into_raw_parts", issue = "80690")] /// 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. /// @@ -355,7 +355,7 @@ pub struct WriterPanicked { 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")] + #[unstable(feature = "bufwriter_into_raw_parts", issue = "80690")] pub fn into_inner(self) -> Vec { self.buf } @@ -364,7 +364,7 @@ impl WriterPanicked { "BufWriter inner writer panicked, what data remains unwritten is not known"; } -#[unstable(feature = "bufwriter_into_raw_parts", issue = "none")] +#[unstable(feature = "bufwriter_into_raw_parts", issue = "80690")] impl error::Error for WriterPanicked { #[allow(deprecated, deprecated_in_future)] fn description(&self) -> &str { @@ -372,14 +372,14 @@ impl error::Error for WriterPanicked { } } -#[unstable(feature = "bufwriter_into_raw_parts", issue = "none")] +#[unstable(feature = "bufwriter_into_raw_parts", issue = "80690")] 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")] +#[unstable(feature = "bufwriter_into_raw_parts", issue = "80690")] impl fmt::Debug for WriterPanicked { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("WriterPanicked")