Auto merge of #155321 - briansmith:b/bufwriter-conservative, r=Mark-Simulacrum

`BufWriter`: Make the soundness of `BorrowedBuf` usage clearer.

The previous safety comment was outdated as it was written before `BorrowedBuf::set_init` was changed to be a boolean. It also failed to address the possibility that `flush_buf` invalidated the assumption.
This commit is contained in:
bors
2026-05-09 18:40:20 +00:00
4 changed files with 25 additions and 3 deletions
+6
View File
@@ -1784,6 +1784,9 @@ pub fn into_boxed_slice(mut self) -> Box<[T], A> {
/// [`drain`]: Vec::drain
#[stable(feature = "rust1", since = "1.0.0")]
pub fn truncate(&mut self, len: usize) {
// SAFETY: `BufWriter::flush_buf` assumes that this will not
// de-initialize any elements of the spare capacity.
// This is safe because:
//
// * the slice passed to `drop_in_place` is valid; the `len > self.len`
@@ -1857,6 +1860,9 @@ pub const fn as_slice(&self) -> &[T] {
#[rustc_diagnostic_item = "vec_as_mut_slice"]
#[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")]
pub const fn as_mut_slice(&mut self) -> &mut [T] {
// SAFETY: `BufWriter::flush_buf` assumes that this will not
// de-initialize any elements of the spare capacity.
// SAFETY: `slice::from_raw_parts_mut` requires pointee is a contiguous, aligned buffer of
// size `len` containing properly-initialized `T`s. Data must not be accessed through any
// other pointer for the returned lifetime. Further, `len * size_of::<T>` <=
+1 -1
View File
@@ -92,7 +92,7 @@ pub fn len(&self) -> usize {
self.filled
}
/// Returns the length of the initialized part of the buffer.
/// Returns `true` if the buffer is initialized.
#[unstable(feature = "borrowed_buf_init", issue = "78485")]
#[inline]
pub fn is_init(&self) -> bool {
+14 -1
View File
@@ -193,6 +193,10 @@ impl<W: ?Sized + Write> BufWriter<W> {
/// `write`), any 0-length writes from `inner` must be reported as i/o
/// errors from this method.
pub(in crate::io) fn flush_buf(&mut self) -> io::Result<()> {
// SAFETY: `<BufWriter as BufferedWriterSpec>::copy_from` assumes that
// this will not de-initialize any elements of `self.buf`'s spare
// capacity.
/// Helper struct to ensure the buffer is updated after all the writes
/// are complete. It tracks the number of written bytes and drains them
/// all from the front of the buffer when dropped.
@@ -225,7 +229,16 @@ fn done(&self) -> bool {
impl Drop for BufGuard<'_> {
fn drop(&mut self) {
if self.written > 0 {
self.buffer.drain(..self.written);
// Like `self.buffer.drain(..self.written)` but more obviously
// preserving the spare capacity; see note above.
let new_len = self.buffer.len() - self.written;
// SAFETY: Assumes `Vec::as_mut_slice` will not
// de-initialize any elements of `self.buf`'s spare capacity,
// and that `<&mut [u8]>::copy_within` will not do so either.
self.buffer.as_mut_slice().copy_within(self.written.., 0);
// SAFETY: Assumes `Vec::truncate` will not de-initialize
// any elements of `self.buf`'s spare capacity,
self.buffer.truncate(new_len);
}
}
}
+4 -1
View File
@@ -221,7 +221,8 @@ fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64> {
let mut read_buf: BorrowedBuf<'_> = buf.spare_capacity_mut().into();
if init {
// SAFETY: init is either 0 or the init_len from the previous iteration.
// SAFETY: `init` is only true after `reader` initializes
// `read_buf`. See the comment about `flush_buf` below.
unsafe { read_buf.set_init() };
}
@@ -248,6 +249,8 @@ fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64> {
Err(e) => return Err(e),
}
} else {
// SAFETY: `flush_buf` will not de-initialize any elements of
// the spare capacity so we can remember `init` across this.
self.flush_buf()?;
}
}