diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index e13add799bcb9..be01a031ff090 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -13,7 +13,7 @@ mod tests;
 
 use crate::ffi::OsString;
 use crate::fmt;
-use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
+use crate::io::{self, IoSlice, IoSliceMut, Read, ReadBuf, Seek, SeekFrom, Write};
 use crate::path::{Path, PathBuf};
 use crate::sys::fs as fs_imp;
 use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
@@ -624,15 +624,13 @@ impl Read for File {
         self.inner.read_vectored(bufs)
     }
 
-    #[inline]
-    fn is_read_vectored(&self) -> bool {
-        self.inner.is_read_vectored()
+    fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+        self.inner.read_buf(buf)
     }
 
     #[inline]
-    unsafe fn initializer(&self) -> Initializer {
-        // SAFETY: Read is guaranteed to work on uninitialized memory
-        unsafe { Initializer::nop() }
+    fn is_read_vectored(&self) -> bool {
+        self.inner.is_read_vectored()
     }
 
     // Reserves space in the buffer based on the file size when available.
@@ -678,6 +676,10 @@ impl Read for &File {
         self.inner.read(buf)
     }
 
+    fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+        self.inner.read_buf(buf)
+    }
+
     fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
         self.inner.read_vectored(bufs)
     }
@@ -687,12 +689,6 @@ impl Read for &File {
         self.inner.is_read_vectored()
     }
 
-    #[inline]
-    unsafe fn initializer(&self) -> Initializer {
-        // SAFETY: Read is guaranteed to work on uninitialized memory
-        unsafe { Initializer::nop() }
-    }
-
     // Reserves space in the buffer based on the file size when available.
     fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
         buf.reserve(buffer_capacity_required(self));
diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs
index 2864e94f60f2c..b56dc65f0b2f3 100644
--- a/library/std/src/io/buffered/bufreader.rs
+++ b/library/std/src/io/buffered/bufreader.rs
@@ -1,8 +1,9 @@
 use crate::cmp;
 use crate::fmt;
 use crate::io::{
-    self, BufRead, Initializer, IoSliceMut, Read, Seek, SeekFrom, SizeHint, DEFAULT_BUF_SIZE,
+    self, BufRead, IoSliceMut, Read, ReadBuf, Seek, SeekFrom, SizeHint, DEFAULT_BUF_SIZE,
 };
+use crate::mem::MaybeUninit;
 
 /// The `BufReader<R>` struct adds buffering to any reader.
 ///
@@ -47,9 +48,10 @@ use crate::io::{
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct BufReader<R> {
     inner: R,
-    buf: Box<[u8]>,
+    buf: Box<[MaybeUninit<u8>]>,
     pos: usize,
     cap: usize,
+    init: usize,
 }
 
 impl<R: Read> BufReader<R> {
@@ -91,11 +93,8 @@ impl<R: Read> BufReader<R> {
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn with_capacity(capacity: usize, inner: R) -> BufReader<R> {
-        unsafe {
-            let mut buf = Box::new_uninit_slice(capacity).assume_init();
-            inner.initializer().initialize(&mut buf);
-            BufReader { inner, buf, pos: 0, cap: 0 }
-        }
+        let buf = Box::new_uninit_slice(capacity);
+        BufReader { inner, buf, pos: 0, cap: 0, init: 0 }
     }
 }
 
@@ -171,7 +170,8 @@ impl<R> BufReader<R> {
     /// ```
     #[stable(feature = "bufreader_buffer", since = "1.37.0")]
     pub fn buffer(&self) -> &[u8] {
-        &self.buf[self.pos..self.cap]
+        // SAFETY: self.cap is always <= self.init, so self.buf[self.pos..self.cap] is always init
+        unsafe { MaybeUninit::slice_assume_init_ref(&self.buf[self.pos..self.cap]) }
     }
 
     /// Returns the number of bytes the internal buffer can hold at once.
@@ -271,6 +271,25 @@ impl<R: Read> Read for BufReader<R> {
         Ok(nread)
     }
 
+    fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+        // If we don't have any buffered data and we're doing a massive read
+        // (larger than our internal buffer), bypass our internal buffer
+        // entirely.
+        if self.pos == self.cap && buf.remaining() >= self.buf.len() {
+            self.discard_buffer();
+            return self.inner.read_buf(buf);
+        }
+
+        let prev = buf.filled_len();
+
+        let mut rem = self.fill_buf()?;
+        rem.read_buf(buf)?;
+
+        self.consume(buf.filled_len() - prev); //slice impl of read_buf known to never unfill buf
+
+        Ok(())
+    }
+
     // Small read_exacts from a BufReader are extremely common when used with a deserializer.
     // The default implementation calls read in a loop, which results in surprisingly poor code
     // generation for the common path where the buffer has enough bytes to fill the passed-in
@@ -303,16 +322,11 @@ impl<R: Read> Read for BufReader<R> {
         self.inner.is_read_vectored()
     }
 
-    // we can't skip unconditionally because of the large buffer case in read.
-    unsafe fn initializer(&self) -> Initializer {
-        self.inner.initializer()
-    }
-
     // The inner reader might have an optimized `read_to_end`. Drain our buffer and then
     // delegate to the inner implementation.
     fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
         let nread = self.cap - self.pos;
-        buf.extend_from_slice(&self.buf[self.pos..self.cap]);
+        buf.extend_from_slice(&self.buffer());
         self.discard_buffer();
         Ok(nread + self.inner.read_to_end(buf)?)
     }
@@ -363,10 +377,23 @@ impl<R: Read> BufRead for BufReader<R> {
         // to tell the compiler that the pos..cap slice is always valid.
         if self.pos >= self.cap {
             debug_assert!(self.pos == self.cap);
-            self.cap = self.inner.read(&mut self.buf)?;
+
+            let mut readbuf = ReadBuf::uninit(&mut self.buf);
+
+            // SAFETY: `self.init` is either 0 or set to `readbuf.initialized_len()`
+            // from the last time this function was called
+            unsafe {
+                readbuf.assume_init(self.init);
+            }
+
+            self.inner.read_buf(&mut readbuf)?;
+
+            self.cap = readbuf.filled_len();
+            self.init = readbuf.initialized_len();
+
             self.pos = 0;
         }
-        Ok(&self.buf[self.pos..self.cap])
+        Ok(self.buffer())
     }
 
     fn consume(&mut self, amt: usize) {
diff --git a/library/std/src/io/buffered/tests.rs b/library/std/src/io/buffered/tests.rs
index feb149c07a525..9d429e7090e83 100644
--- a/library/std/src/io/buffered/tests.rs
+++ b/library/std/src/io/buffered/tests.rs
@@ -1,5 +1,6 @@
 use crate::io::prelude::*;
-use crate::io::{self, BufReader, BufWriter, ErrorKind, IoSlice, LineWriter, SeekFrom};
+use crate::io::{self, BufReader, BufWriter, ErrorKind, IoSlice, LineWriter, ReadBuf, SeekFrom};
+use crate::mem::MaybeUninit;
 use crate::panic;
 use crate::sync::atomic::{AtomicUsize, Ordering};
 use crate::thread;
@@ -55,6 +56,55 @@ fn test_buffered_reader() {
     assert_eq!(reader.read(&mut buf).unwrap(), 0);
 }
 
+#[test]
+fn test_buffered_reader_read_buf() {
+    let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4];
+    let mut reader = BufReader::with_capacity(2, inner);
+
+    let mut buf = [MaybeUninit::uninit(); 3];
+    let mut buf = ReadBuf::uninit(&mut buf);
+
+    reader.read_buf(&mut buf).unwrap();
+
+    assert_eq!(buf.filled(), [5, 6, 7]);
+    assert_eq!(reader.buffer(), []);
+
+    let mut buf = [MaybeUninit::uninit(); 2];
+    let mut buf = ReadBuf::uninit(&mut buf);
+
+    reader.read_buf(&mut buf).unwrap();
+
+    assert_eq!(buf.filled(), [0, 1]);
+    assert_eq!(reader.buffer(), []);
+
+    let mut buf = [MaybeUninit::uninit(); 1];
+    let mut buf = ReadBuf::uninit(&mut buf);
+
+    reader.read_buf(&mut buf).unwrap();
+
+    assert_eq!(buf.filled(), [2]);
+    assert_eq!(reader.buffer(), [3]);
+
+    let mut buf = [MaybeUninit::uninit(); 3];
+    let mut buf = ReadBuf::uninit(&mut buf);
+
+    reader.read_buf(&mut buf).unwrap();
+
+    assert_eq!(buf.filled(), [3]);
+    assert_eq!(reader.buffer(), []);
+
+    reader.read_buf(&mut buf).unwrap();
+
+    assert_eq!(buf.filled(), [3, 4]);
+    assert_eq!(reader.buffer(), []);
+
+    buf.clear();
+
+    reader.read_buf(&mut buf).unwrap();
+
+    assert_eq!(buf.filled_len(), 0);
+}
+
 #[test]
 fn test_buffered_reader_seek() {
     let inner: &[u8] = &[5, 6, 7, 0, 1, 2, 3, 4];
diff --git a/library/std/src/io/copy.rs b/library/std/src/io/copy.rs
index eb60df214c489..6ab9666230524 100644
--- a/library/std/src/io/copy.rs
+++ b/library/std/src/io/copy.rs
@@ -1,4 +1,4 @@
-use super::{BufWriter, ErrorKind, Read, Result, Write, DEFAULT_BUF_SIZE};
+use super::{BufWriter, ErrorKind, Read, ReadBuf, Result, Write, DEFAULT_BUF_SIZE};
 use crate::mem::MaybeUninit;
 
 /// Copies the entire contents of a reader into a writer.
@@ -82,33 +82,30 @@ impl<I: Write> BufferedCopySpec for BufWriter<I> {
             return stack_buffer_copy(reader, writer);
         }
 
-        // FIXME: #42788
-        //
-        //   - This creates a (mut) reference to a slice of
-        //     _uninitialized_ integers, which is **undefined behavior**
-        //
-        //   - Only the standard library gets to soundly "ignore" this,
-        //     based on its privileged knowledge of unstable rustc
-        //     internals;
-        unsafe {
-            let spare_cap = writer.buffer_mut().spare_capacity_mut();
-            reader.initializer().initialize(MaybeUninit::slice_assume_init_mut(spare_cap));
-        }
-
         let mut len = 0;
+        let mut init = 0;
 
         loop {
             let buf = writer.buffer_mut();
-            let spare_cap = buf.spare_capacity_mut();
-
-            if spare_cap.len() >= DEFAULT_BUF_SIZE {
-                match reader.read(unsafe { MaybeUninit::slice_assume_init_mut(spare_cap) }) {
-                    Ok(0) => return Ok(len), // EOF reached
-                    Ok(bytes_read) => {
-                        assert!(bytes_read <= spare_cap.len());
-                        // SAFETY: The initializer contract guarantees that either it or `read`
-                        // will have initialized these bytes. And we just checked that the number
-                        // of bytes is within the buffer capacity.
+            let mut read_buf = ReadBuf::uninit(buf.spare_capacity_mut());
+
+            // SAFETY: init is either 0 or the initialized_len of the previous iteration
+            unsafe {
+                read_buf.assume_init(init);
+            }
+
+            if read_buf.capacity() >= DEFAULT_BUF_SIZE {
+                match reader.read_buf(&mut read_buf) {
+                    Ok(()) => {
+                        let bytes_read = read_buf.filled_len();
+
+                        if bytes_read == 0 {
+                            return Ok(len);
+                        }
+
+                        init = read_buf.initialized_len() - bytes_read;
+
+                        // SAFETY: ReadBuf guarantees all of its filled bytes are init
                         unsafe { buf.set_len(buf.len() + bytes_read) };
                         len += bytes_read as u64;
                         // Read again if the buffer still has enough capacity, as BufWriter itself would do
@@ -129,28 +126,26 @@ fn stack_buffer_copy<R: Read + ?Sized, W: Write + ?Sized>(
     reader: &mut R,
     writer: &mut W,
 ) -> Result<u64> {
-    let mut buf = MaybeUninit::<[u8; DEFAULT_BUF_SIZE]>::uninit();
-    // FIXME: #42788
-    //
-    //   - This creates a (mut) reference to a slice of
-    //     _uninitialized_ integers, which is **undefined behavior**
-    //
-    //   - Only the standard library gets to soundly "ignore" this,
-    //     based on its privileged knowledge of unstable rustc
-    //     internals;
-    unsafe {
-        reader.initializer().initialize(buf.assume_init_mut());
-    }
+    let mut buf = [MaybeUninit::uninit(); DEFAULT_BUF_SIZE];
+    let mut buf = ReadBuf::uninit(&mut buf);
+
+    let mut len = 0;
 
-    let mut written = 0;
     loop {
-        let len = match reader.read(unsafe { buf.assume_init_mut() }) {
-            Ok(0) => return Ok(written),
-            Ok(len) => len,
-            Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
+        match reader.read_buf(&mut buf) {
+            Ok(()) => {}
+            Err(e) if e.kind() == ErrorKind::Interrupted => continue,
             Err(e) => return Err(e),
         };
-        writer.write_all(unsafe { &buf.assume_init_ref()[..len] })?;
-        written += len as u64;
+
+        if buf.filled().is_empty() {
+            break;
+        }
+
+        len += buf.filled().len() as u64;
+        writer.write_all(buf.filled())?;
+        buf.clear();
     }
+
+    Ok(len)
 }
diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs
index 980b2531192e8..416cc906e65a5 100644
--- a/library/std/src/io/cursor.rs
+++ b/library/std/src/io/cursor.rs
@@ -4,7 +4,7 @@ mod tests;
 use crate::io::prelude::*;
 
 use crate::cmp;
-use crate::io::{self, Error, ErrorKind, Initializer, IoSlice, IoSliceMut, SeekFrom};
+use crate::io::{self, Error, ErrorKind, IoSlice, IoSliceMut, ReadBuf, SeekFrom};
 
 use core::convert::TryInto;
 
@@ -324,6 +324,16 @@ where
         Ok(n)
     }
 
+    fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+        let prev_filled = buf.filled_len();
+
+        Read::read_buf(&mut self.fill_buf()?, buf)?;
+
+        self.pos += (buf.filled_len() - prev_filled) as u64;
+
+        Ok(())
+    }
+
     fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
         let mut nread = 0;
         for buf in bufs {
@@ -346,11 +356,6 @@ where
         self.pos += n as u64;
         Ok(())
     }
-
-    #[inline]
-    unsafe fn initializer(&self) -> Initializer {
-        Initializer::nop()
-    }
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs
index 7a2a49ba7d707..23201f9fc5c94 100644
--- a/library/std/src/io/impls.rs
+++ b/library/std/src/io/impls.rs
@@ -5,7 +5,7 @@ use crate::alloc::Allocator;
 use crate::cmp;
 use crate::fmt;
 use crate::io::{
-    self, BufRead, Error, ErrorKind, Initializer, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write,
+    self, BufRead, Error, ErrorKind, IoSlice, IoSliceMut, Read, ReadBuf, Seek, SeekFrom, Write,
 };
 use crate::mem;
 
@@ -19,6 +19,11 @@ impl<R: Read + ?Sized> Read for &mut R {
         (**self).read(buf)
     }
 
+    #[inline]
+    fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+        (**self).read_buf(buf)
+    }
+
     #[inline]
     fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
         (**self).read_vectored(bufs)
@@ -29,11 +34,6 @@ impl<R: Read + ?Sized> Read for &mut R {
         (**self).is_read_vectored()
     }
 
-    #[inline]
-    unsafe fn initializer(&self) -> Initializer {
-        (**self).initializer()
-    }
-
     #[inline]
     fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
         (**self).read_to_end(buf)
@@ -123,6 +123,11 @@ impl<R: Read + ?Sized> Read for Box<R> {
         (**self).read(buf)
     }
 
+    #[inline]
+    fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+        (**self).read_buf(buf)
+    }
+
     #[inline]
     fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
         (**self).read_vectored(bufs)
@@ -133,11 +138,6 @@ impl<R: Read + ?Sized> Read for Box<R> {
         (**self).is_read_vectored()
     }
 
-    #[inline]
-    unsafe fn initializer(&self) -> Initializer {
-        (**self).initializer()
-    }
-
     #[inline]
     fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
         (**self).read_to_end(buf)
@@ -247,6 +247,17 @@ impl Read for &[u8] {
         Ok(amt)
     }
 
+    #[inline]
+    fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+        let amt = cmp::min(buf.remaining(), self.len());
+        let (a, b) = self.split_at(amt);
+
+        buf.append(a);
+
+        *self = b;
+        Ok(())
+    }
+
     #[inline]
     fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
         let mut nread = 0;
@@ -265,11 +276,6 @@ impl Read for &[u8] {
         true
     }
 
-    #[inline]
-    unsafe fn initializer(&self) -> Initializer {
-        Initializer::nop()
-    }
-
     #[inline]
     fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
         if buf.len() > self.len() {
diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs
index 8cc91566418dc..dd182c059b9cb 100644
--- a/library/std/src/io/mod.rs
+++ b/library/std/src/io/mod.rs
@@ -256,7 +256,6 @@ use crate::convert::TryInto;
 use crate::fmt;
 use crate::mem::replace;
 use crate::ops::{Deref, DerefMut};
-use crate::ptr;
 use crate::slice;
 use crate::str;
 use crate::sys;
@@ -288,12 +287,16 @@ pub use self::stdio::{_eprint, _print};
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::util::{empty, repeat, sink, Empty, Repeat, Sink};
 
+#[unstable(feature = "read_buf", issue = "78485")]
+pub use self::readbuf::ReadBuf;
+
 mod buffered;
 pub(crate) mod copy;
 mod cursor;
 mod error;
 mod impls;
 pub mod prelude;
+mod readbuf;
 mod stdio;
 mod util;
 
@@ -355,52 +358,43 @@ where
 // of data to return. Simply tacking on an extra DEFAULT_BUF_SIZE space every
 // time is 4,500 times (!) slower than a default reservation size of 32 if the
 // reader has a very small amount of data to return.
-//
-// Because we're extending the buffer with uninitialized data for trusted
-// readers, we need to make sure to truncate that if any of this panics.
 pub(crate) fn default_read_to_end<R: Read + ?Sized>(r: &mut R, buf: &mut Vec<u8>) -> Result<usize> {
     let start_len = buf.len();
     let start_cap = buf.capacity();
-    let mut g = Guard { len: buf.len(), buf };
+
+    let mut initialized = 0; // Extra initalized bytes from previous loop iteration
     loop {
-        // If we've read all the way up to the capacity, reserve more space.
-        if g.len == g.buf.capacity() {
-            g.buf.reserve(32);
+        if buf.len() == buf.capacity() {
+            buf.reserve(32); // buf is full, need more space
         }
 
-        // Initialize any excess capacity and adjust the length so we can write
-        // to it.
-        if g.buf.len() < g.buf.capacity() {
-            unsafe {
-                // FIXME(danielhenrymantilla): #42788
-                //
-                //   - This creates a (mut) reference to a slice of
-                //     _uninitialized_ integers, which is **undefined behavior**
-                //
-                //   - Only the standard library gets to soundly "ignore" this,
-                //     based on its privileged knowledge of unstable rustc
-                //     internals;
-                let capacity = g.buf.capacity();
-                g.buf.set_len(capacity);
-                r.initializer().initialize(&mut g.buf[g.len..]);
-            }
+        let mut read_buf = ReadBuf::uninit(buf.spare_capacity_mut());
+
+        // SAFETY: These bytes were initalized but not filled in the previous loop
+        unsafe {
+            read_buf.assume_init(initialized);
         }
 
-        let buf = &mut g.buf[g.len..];
-        match r.read(buf) {
-            Ok(0) => return Ok(g.len - start_len),
-            Ok(n) => {
-                // We can't allow bogus values from read. If it is too large, the returned vec could have its length
-                // set past its capacity, or if it overflows the vec could be shortened which could create an invalid
-                // string if this is called via read_to_string.
-                assert!(n <= buf.len());
-                g.len += n;
-            }
-            Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
+        match r.read_buf(&mut read_buf) {
+            Ok(()) => {}
+            Err(e) if e.kind() == ErrorKind::Interrupted => continue,
             Err(e) => return Err(e),
         }
 
-        if g.len == g.buf.capacity() && g.buf.capacity() == start_cap {
+        if read_buf.filled_len() == 0 {
+            return Ok(buf.len() - start_len);
+        }
+
+        // store how much was initialized but not filled
+        initialized = read_buf.initialized_len() - read_buf.filled_len();
+        let new_len = read_buf.filled_len() + buf.len();
+
+        // SAFETY: ReadBuf's invariants mean this much memory is init
+        unsafe {
+            buf.set_len(new_len);
+        }
+
+        if buf.len() == buf.capacity() && buf.capacity() == start_cap {
             // The buffer might be an exact fit. Let's read into a probe buffer
             // and see if it returns `Ok(0)`. If so, we've avoided an
             // unnecessary doubling of the capacity. But if not, append the
@@ -409,10 +403,9 @@ pub(crate) fn default_read_to_end<R: Read + ?Sized>(r: &mut R, buf: &mut Vec<u8>
 
             loop {
                 match r.read(&mut probe) {
-                    Ok(0) => return Ok(g.len - start_len),
+                    Ok(0) => return Ok(buf.len() - start_len),
                     Ok(n) => {
-                        g.buf.extend_from_slice(&probe[..n]);
-                        g.len += n;
+                        buf.extend_from_slice(&probe[..n]);
                         break;
                     }
                     Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
@@ -474,6 +467,15 @@ pub(crate) fn default_read_exact<R: Read + ?Sized>(this: &mut R, mut buf: &mut [
     }
 }
 
+pub(crate) fn default_read_buf<F>(read: F, buf: &mut ReadBuf<'_>) -> Result<()>
+where
+    F: FnOnce(&mut [u8]) -> Result<usize>,
+{
+    let n = read(buf.initialize_unfilled())?;
+    buf.add_filled(n);
+    Ok(())
+}
+
 /// The `Read` trait allows for reading bytes from a source.
 ///
 /// Implementors of the `Read` trait are called 'readers'.
@@ -656,31 +658,6 @@ pub trait Read {
         false
     }
 
-    /// Determines if this `Read`er can work with buffers of uninitialized
-    /// memory.
-    ///
-    /// The default implementation returns an initializer which will zero
-    /// buffers.
-    ///
-    /// If a `Read`er guarantees that it can work properly with uninitialized
-    /// memory, it should call [`Initializer::nop()`]. See the documentation for
-    /// [`Initializer`] for details.
-    ///
-    /// The behavior of this method must be independent of the state of the
-    /// `Read`er - the method only takes `&self` so that it can be used through
-    /// trait objects.
-    ///
-    /// # Safety
-    ///
-    /// This method is unsafe because a `Read`er could otherwise return a
-    /// non-zeroing `Initializer` from another `Read` type without an `unsafe`
-    /// block.
-    #[unstable(feature = "read_initializer", issue = "42788")]
-    #[inline]
-    unsafe fn initializer(&self) -> Initializer {
-        Initializer::zeroing()
-    }
-
     /// Read all bytes until EOF in this source, placing them into `buf`.
     ///
     /// All bytes read from this source will be appended to the specified buffer
@@ -830,7 +807,40 @@ pub trait Read {
         default_read_exact(self, buf)
     }
 
-    /// Creates a "by reference" adapter for this instance of `Read`.
+    /// Pull some bytes from this source into the specified buffer.
+    ///
+    /// This is equivalent to the [`read`](Read::read) method, except that it is passed a [`ReadBuf`] rather than `[u8]` to allow use
+    /// with uninitialized buffers. The new data will be appended to any existing contents of `buf`.
+    ///
+    /// The default implementation delegates to `read`.
+    #[unstable(feature = "read_buf", issue = "78485")]
+    fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> Result<()> {
+        default_read_buf(|b| self.read(b), buf)
+    }
+
+    /// Read the exact number of bytes required to fill `buf`.
+    ///
+    /// This is equivalent to the [`read_exact`](Read::read_exact) method, except that it is passed a [`ReadBuf`] rather than `[u8]` to
+    /// allow use with uninitialized buffers.
+    #[unstable(feature = "read_buf", issue = "78485")]
+    fn read_buf_exact(&mut self, buf: &mut ReadBuf<'_>) -> Result<()> {
+        while buf.remaining() > 0 {
+            let prev_filled = buf.filled().len();
+            match self.read_buf(buf) {
+                Ok(()) => {}
+                Err(e) if e.kind() == ErrorKind::Interrupted => continue,
+                Err(e) => return Err(e),
+            }
+
+            if buf.filled().len() == prev_filled {
+                return Err(Error::new(ErrorKind::UnexpectedEof, "failed to fill buffer"));
+            }
+        }
+
+        Ok(())
+    }
+
+    /// Creates a "by reference" adaptor for this instance of `Read`.
     ///
     /// The returned adapter also implements `Read` and will simply borrow this
     /// current reader.
@@ -1300,53 +1310,6 @@ impl<'a> Deref for IoSlice<'a> {
     }
 }
 
-/// A type used to conditionally initialize buffers passed to `Read` methods.
-#[unstable(feature = "read_initializer", issue = "42788")]
-#[derive(Debug)]
-pub struct Initializer(bool);
-
-impl Initializer {
-    /// Returns a new `Initializer` which will zero out buffers.
-    #[unstable(feature = "read_initializer", issue = "42788")]
-    #[must_use]
-    #[inline]
-    pub fn zeroing() -> Initializer {
-        Initializer(true)
-    }
-
-    /// Returns a new `Initializer` which will not zero out buffers.
-    ///
-    /// # Safety
-    ///
-    /// This may only be called by `Read`ers which guarantee that they will not
-    /// read from buffers passed to `Read` methods, and that the return value of
-    /// the method accurately reflects the number of bytes that have been
-    /// written to the head of the buffer.
-    #[unstable(feature = "read_initializer", issue = "42788")]
-    #[must_use]
-    #[inline]
-    pub unsafe fn nop() -> Initializer {
-        Initializer(false)
-    }
-
-    /// Indicates if a buffer should be initialized.
-    #[unstable(feature = "read_initializer", issue = "42788")]
-    #[must_use]
-    #[inline]
-    pub fn should_initialize(&self) -> bool {
-        self.0
-    }
-
-    /// Initializes a buffer if necessary.
-    #[unstable(feature = "read_initializer", issue = "42788")]
-    #[inline]
-    pub fn initialize(&self, buf: &mut [u8]) {
-        if self.should_initialize() {
-            unsafe { ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len()) }
-        }
-    }
-}
-
 /// A trait for objects which are byte-oriented sinks.
 ///
 /// Implementors of the `Write` trait are sometimes called 'writers'.
@@ -2403,11 +2366,6 @@ impl<T: Read, U: Read> Read for Chain<T, U> {
         }
         self.second.read_vectored(bufs)
     }
-
-    unsafe fn initializer(&self) -> Initializer {
-        let initializer = self.first.initializer();
-        if initializer.should_initialize() { initializer } else { self.second.initializer() }
-    }
 }
 
 #[stable(feature = "chain_bufread", since = "1.9.0")]
@@ -2610,8 +2568,53 @@ impl<T: Read> Read for Take<T> {
         Ok(n)
     }
 
-    unsafe fn initializer(&self) -> Initializer {
-        self.inner.initializer()
+    fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> Result<()> {
+        // Don't call into inner reader at all at EOF because it may still block
+        if self.limit == 0 {
+            return Ok(());
+        }
+
+        let prev_filled = buf.filled_len();
+
+        if self.limit <= buf.remaining() as u64 {
+            // if we just use an as cast to convert, limit may wrap around on a 32 bit target
+            let limit = cmp::min(self.limit, usize::MAX as u64) as usize;
+
+            let extra_init = cmp::min(limit as usize, buf.initialized_len() - buf.filled_len());
+
+            // SAFETY: no uninit data is written to ibuf
+            let ibuf = unsafe { &mut buf.unfilled_mut()[..limit] };
+
+            let mut sliced_buf = ReadBuf::uninit(ibuf);
+
+            // SAFETY: extra_init bytes of ibuf are known to be initialized
+            unsafe {
+                sliced_buf.assume_init(extra_init);
+            }
+
+            self.inner.read_buf(&mut sliced_buf)?;
+
+            let new_init = sliced_buf.initialized_len();
+            let filled = sliced_buf.filled_len();
+
+            // sliced_buf / ibuf must drop here
+
+            // SAFETY: new_init bytes of buf's unfilled buffer have been initialized
+            unsafe {
+                buf.assume_init(new_init);
+            }
+
+            buf.add_filled(filled);
+
+            self.limit -= filled as u64;
+        } else {
+            self.inner.read_buf(buf)?;
+
+            //inner may unfill
+            self.limit -= buf.filled_len().saturating_sub(prev_filled) as u64;
+        }
+
+        Ok(())
     }
 }
 
diff --git a/library/std/src/io/readbuf.rs b/library/std/src/io/readbuf.rs
new file mode 100644
index 0000000000000..d84a500e07800
--- /dev/null
+++ b/library/std/src/io/readbuf.rs
@@ -0,0 +1,245 @@
+#![unstable(feature = "read_buf", issue = "78485")]
+
+#[cfg(test)]
+mod tests;
+
+use crate::cmp;
+use crate::fmt::{self, Debug, Formatter};
+use crate::mem::MaybeUninit;
+
+/// A wrapper around a byte buffer that is incrementally filled and initialized.
+///
+/// This type is a sort of "double cursor". It tracks three regions in the buffer: a region at the beginning of the
+/// buffer that has been logically filled with data, a region that has been initialized at some point but not yet
+/// logically filled, and a region at the end that is fully uninitialized. The filled region is guaranteed to be a
+/// subset of the initialized region.
+///
+/// In summary, the contents of the buffer can be visualized as:
+/// ```not_rust
+/// [             capacity              ]
+/// [ filled |         unfilled         ]
+/// [    initialized    | uninitialized ]
+/// ```
+pub struct ReadBuf<'a> {
+    buf: &'a mut [MaybeUninit<u8>],
+    filled: usize,
+    initialized: usize,
+}
+
+impl Debug for ReadBuf<'_> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        f.debug_struct("ReadBuf")
+            .field("init", &self.initialized())
+            .field("filled", &self.filled)
+            .field("capacity", &self.capacity())
+            .finish()
+    }
+}
+
+impl<'a> ReadBuf<'a> {
+    /// Creates a new `ReadBuf` from a fully initialized buffer.
+    #[inline]
+    pub fn new(buf: &'a mut [u8]) -> ReadBuf<'a> {
+        let len = buf.len();
+
+        ReadBuf {
+            //SAFETY: initialized data never becoming uninitialized is an invariant of ReadBuf
+            buf: unsafe { (buf as *mut [u8]).as_uninit_slice_mut().unwrap() },
+            filled: 0,
+            initialized: len,
+        }
+    }
+
+    /// Creates a new `ReadBuf` from a fully uninitialized buffer.
+    ///
+    /// Use `assume_init` if part of the buffer is known to be already inintialized.
+    #[inline]
+    pub fn uninit(buf: &'a mut [MaybeUninit<u8>]) -> ReadBuf<'a> {
+        ReadBuf { buf, filled: 0, initialized: 0 }
+    }
+
+    /// Returns the total capacity of the buffer.
+    #[inline]
+    pub fn capacity(&self) -> usize {
+        self.buf.len()
+    }
+
+    /// Returns a shared reference to the filled portion of the buffer.
+    #[inline]
+    pub fn filled(&self) -> &[u8] {
+        //SAFETY: We only slice the filled part of the buffer, which is always valid
+        unsafe { MaybeUninit::slice_assume_init_ref(&self.buf[0..self.filled]) }
+    }
+
+    /// Returns a mutable reference to the filled portion of the buffer.
+    #[inline]
+    pub fn filled_mut(&mut self) -> &mut [u8] {
+        //SAFETY: We only slice the filled part of the buffer, which is always valid
+        unsafe { MaybeUninit::slice_assume_init_mut(&mut self.buf[0..self.filled]) }
+    }
+
+    /// Returns a shared reference to the initialized portion of the buffer.
+    ///
+    /// This includes the filled portion.
+    #[inline]
+    pub fn initialized(&self) -> &[u8] {
+        //SAFETY: We only slice the initialized part of the buffer, which is always valid
+        unsafe { MaybeUninit::slice_assume_init_ref(&self.buf[0..self.initialized]) }
+    }
+
+    /// Returns a mutable reference to the initialized portion of the buffer.
+    ///
+    /// This includes the filled portion.
+    #[inline]
+    pub fn initialized_mut(&mut self) -> &mut [u8] {
+        //SAFETY: We only slice the initialized part of the buffer, which is always valid
+        unsafe { MaybeUninit::slice_assume_init_mut(&mut self.buf[0..self.initialized]) }
+    }
+
+    /// Returns a mutable reference to the unfilled part of the buffer without ensuring that it has been fully
+    /// initialized.
+    ///
+    /// # Safety
+    ///
+    /// The caller must not de-initialize portions of the buffer that have already been initialized.
+    #[inline]
+    pub unsafe fn unfilled_mut(&mut self) -> &mut [MaybeUninit<u8>] {
+        &mut self.buf[self.filled..]
+    }
+
+    /// Returns a mutable reference to the uninitialized part of the buffer.
+    ///
+    /// It is safe to uninitialize any of these bytes.
+    #[inline]
+    pub fn uninitialized_mut(&mut self) -> &mut [MaybeUninit<u8>] {
+        &mut self.buf[self.initialized..]
+    }
+
+    /// Returns a mutable reference to the unfilled part of the buffer, ensuring it is fully initialized.
+    ///
+    /// Since `ReadBuf` tracks the region of the buffer that has been initialized, this is effectively "free" after
+    /// the first use.
+    #[inline]
+    pub fn initialize_unfilled(&mut self) -> &mut [u8] {
+        // should optimize out the assertion
+        self.initialize_unfilled_to(self.remaining())
+    }
+
+    /// Returns a mutable reference to the first `n` bytes of the unfilled part of the buffer, ensuring it is
+    /// fully initialized.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `self.remaining()` is less than `n`.
+    #[inline]
+    pub fn initialize_unfilled_to(&mut self, n: usize) -> &mut [u8] {
+        assert!(self.remaining() >= n);
+
+        let extra_init = self.initialized - self.filled;
+        // If we don't have enough initialized, do zeroing
+        if n > extra_init {
+            let uninit = n - extra_init;
+            let unfilled = &mut self.uninitialized_mut()[0..uninit];
+
+            for byte in unfilled.iter_mut() {
+                byte.write(0);
+            }
+
+            // SAFETY: we just inintialized uninit bytes, and the previous bytes were already init
+            unsafe {
+                self.assume_init(n);
+            }
+        }
+
+        let filled = self.filled;
+
+        &mut self.initialized_mut()[filled..filled + n]
+    }
+
+    /// Returns the number of bytes at the end of the slice that have not yet been filled.
+    #[inline]
+    pub fn remaining(&self) -> usize {
+        self.capacity() - self.filled
+    }
+
+    /// Clears the buffer, resetting the filled region to empty.
+    ///
+    /// The number of initialized bytes is not changed, and the contents of the buffer are not modified.
+    #[inline]
+    pub fn clear(&mut self) {
+        self.set_filled(0); // The assertion in `set_filled` is optimized out
+    }
+
+    /// Increases the size of the filled region of the buffer.
+    ///
+    /// The number of initialized bytes is not changed.
+    ///
+    /// # Panics
+    ///
+    /// Panics if the filled region of the buffer would become larger than the initialized region.
+    #[inline]
+    pub fn add_filled(&mut self, n: usize) {
+        self.set_filled(self.filled + n);
+    }
+
+    /// Sets the size of the filled region of the buffer.
+    ///
+    /// The number of initialized bytes is not changed.
+    ///
+    /// Note that this can be used to *shrink* the filled region of the buffer in addition to growing it (for
+    /// example, by a `Read` implementation that compresses data in-place).
+    ///
+    /// # Panics
+    ///
+    /// Panics if the filled region of the buffer would become larger than the initialized region.
+    #[inline]
+    pub fn set_filled(&mut self, n: usize) {
+        assert!(n <= self.initialized);
+
+        self.filled = n;
+    }
+
+    /// Asserts that the first `n` unfilled bytes of the buffer are initialized.
+    ///
+    /// `ReadBuf` assumes that bytes are never de-initialized, so this method does nothing when called with fewer
+    /// bytes than are already known to be initialized.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensure that the first `n` unfilled bytes of the buffer have already been initialized.
+    #[inline]
+    pub unsafe fn assume_init(&mut self, n: usize) {
+        self.initialized = cmp::max(self.initialized, self.filled + n);
+    }
+
+    /// Appends data to the buffer, advancing the written position and possibly also the initialized position.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `self.remaining()` is less than `buf.len()`.
+    #[inline]
+    pub fn append(&mut self, buf: &[u8]) {
+        assert!(self.remaining() >= buf.len());
+
+        // SAFETY: we do not de-initialize any of the elements of the slice
+        unsafe {
+            MaybeUninit::write_slice(&mut self.unfilled_mut()[..buf.len()], buf);
+        }
+
+        // SAFETY: We just added the entire contents of buf to the filled section.
+        unsafe { self.assume_init(buf.len()) }
+        self.add_filled(buf.len());
+    }
+
+    /// Returns the amount of bytes that have been filled.
+    #[inline]
+    pub fn filled_len(&self) -> usize {
+        self.filled
+    }
+
+    /// Returns the amount of bytes that have been initialized.
+    #[inline]
+    pub fn initialized_len(&self) -> usize {
+        self.initialized
+    }
+}
diff --git a/library/std/src/io/readbuf/tests.rs b/library/std/src/io/readbuf/tests.rs
new file mode 100644
index 0000000000000..3b7a5a56d2252
--- /dev/null
+++ b/library/std/src/io/readbuf/tests.rs
@@ -0,0 +1,181 @@
+use super::ReadBuf;
+use crate::mem::MaybeUninit;
+
+/// Test that ReadBuf has the correct numbers when created with new
+#[test]
+fn new() {
+    let mut buf = [0; 16];
+    let rbuf = ReadBuf::new(&mut buf);
+
+    assert_eq!(rbuf.filled_len(), 0);
+    assert_eq!(rbuf.initialized_len(), 16);
+    assert_eq!(rbuf.capacity(), 16);
+    assert_eq!(rbuf.remaining(), 16);
+}
+
+/// Test that ReadBuf has the correct numbers when created with uninit
+#[test]
+fn uninit() {
+    let mut buf = [MaybeUninit::uninit(); 16];
+    let rbuf = ReadBuf::uninit(&mut buf);
+
+    assert_eq!(rbuf.filled_len(), 0);
+    assert_eq!(rbuf.initialized_len(), 0);
+    assert_eq!(rbuf.capacity(), 16);
+    assert_eq!(rbuf.remaining(), 16);
+}
+
+#[test]
+fn initialize_unfilled() {
+    let mut buf = [MaybeUninit::uninit(); 16];
+    let mut rbuf = ReadBuf::uninit(&mut buf);
+
+    rbuf.initialize_unfilled();
+
+    assert_eq!(rbuf.initialized_len(), 16);
+}
+
+#[test]
+fn initialize_unfilled_to() {
+    let mut buf = [MaybeUninit::uninit(); 16];
+    let mut rbuf = ReadBuf::uninit(&mut buf);
+
+    rbuf.initialize_unfilled_to(8);
+
+    assert_eq!(rbuf.initialized_len(), 8);
+
+    rbuf.initialize_unfilled_to(4);
+
+    assert_eq!(rbuf.initialized_len(), 8);
+
+    rbuf.set_filled(8);
+
+    rbuf.initialize_unfilled_to(6);
+
+    assert_eq!(rbuf.initialized_len(), 14);
+
+    rbuf.initialize_unfilled_to(8);
+
+    assert_eq!(rbuf.initialized_len(), 16);
+}
+
+#[test]
+fn add_filled() {
+    let mut buf = [0; 16];
+    let mut rbuf = ReadBuf::new(&mut buf);
+
+    rbuf.add_filled(1);
+
+    assert_eq!(rbuf.filled_len(), 1);
+    assert_eq!(rbuf.remaining(), 15);
+}
+
+#[test]
+#[should_panic]
+fn add_filled_panic() {
+    let mut buf = [MaybeUninit::uninit(); 16];
+    let mut rbuf = ReadBuf::uninit(&mut buf);
+
+    rbuf.add_filled(1);
+}
+
+#[test]
+fn set_filled() {
+    let mut buf = [0; 16];
+    let mut rbuf = ReadBuf::new(&mut buf);
+
+    rbuf.set_filled(16);
+
+    assert_eq!(rbuf.filled_len(), 16);
+    assert_eq!(rbuf.remaining(), 0);
+
+    rbuf.set_filled(6);
+
+    assert_eq!(rbuf.filled_len(), 6);
+    assert_eq!(rbuf.remaining(), 10);
+}
+
+#[test]
+#[should_panic]
+fn set_filled_panic() {
+    let mut buf = [MaybeUninit::uninit(); 16];
+    let mut rbuf = ReadBuf::uninit(&mut buf);
+
+    rbuf.set_filled(16);
+}
+
+#[test]
+fn clear() {
+    let mut buf = [255; 16];
+    let mut rbuf = ReadBuf::new(&mut buf);
+
+    rbuf.set_filled(16);
+
+    assert_eq!(rbuf.filled_len(), 16);
+    assert_eq!(rbuf.remaining(), 0);
+
+    rbuf.clear();
+
+    assert_eq!(rbuf.filled_len(), 0);
+    assert_eq!(rbuf.remaining(), 16);
+
+    assert_eq!(rbuf.initialized(), [255; 16]);
+}
+
+#[test]
+fn assume_init() {
+    let mut buf = [MaybeUninit::uninit(); 16];
+    let mut rbuf = ReadBuf::uninit(&mut buf);
+
+    unsafe {
+        rbuf.assume_init(8);
+    }
+
+    assert_eq!(rbuf.initialized_len(), 8);
+
+    rbuf.add_filled(4);
+
+    unsafe {
+        rbuf.assume_init(2);
+    }
+
+    assert_eq!(rbuf.initialized_len(), 8);
+
+    unsafe {
+        rbuf.assume_init(8);
+    }
+
+    assert_eq!(rbuf.initialized_len(), 12);
+}
+
+#[test]
+fn append() {
+    let mut buf = [MaybeUninit::new(255); 16];
+    let mut rbuf = ReadBuf::uninit(&mut buf);
+
+    rbuf.append(&[0; 8]);
+
+    assert_eq!(rbuf.initialized_len(), 8);
+    assert_eq!(rbuf.filled_len(), 8);
+    assert_eq!(rbuf.filled(), [0; 8]);
+
+    rbuf.clear();
+
+    rbuf.append(&[1; 16]);
+
+    assert_eq!(rbuf.initialized_len(), 16);
+    assert_eq!(rbuf.filled_len(), 16);
+    assert_eq!(rbuf.filled(), [1; 16]);
+}
+
+#[test]
+fn filled_mut() {
+    let mut buf = [0; 16];
+    let mut rbuf = ReadBuf::new(&mut buf);
+
+    rbuf.add_filled(8);
+
+    let filled = rbuf.filled().to_vec();
+
+    assert_eq!(&*filled, &*rbuf.filled_mut());
+}
diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs
index f7fc23c1e829c..9888d3a09c48d 100644
--- a/library/std/src/io/stdio.rs
+++ b/library/std/src/io/stdio.rs
@@ -7,7 +7,7 @@ use crate::io::prelude::*;
 
 use crate::cell::{Cell, RefCell};
 use crate::fmt;
-use crate::io::{self, BufReader, Initializer, IoSlice, IoSliceMut, LineWriter, Lines, Split};
+use crate::io::{self, BufReader, IoSlice, IoSliceMut, LineWriter, Lines, Split};
 use crate::lazy::SyncOnceCell;
 use crate::pin::Pin;
 use crate::sync::atomic::{AtomicBool, Ordering};
@@ -108,11 +108,6 @@ impl Read for StdinRaw {
         self.0.is_read_vectored()
     }
 
-    #[inline]
-    unsafe fn initializer(&self) -> Initializer {
-        Initializer::nop()
-    }
-
     fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
         handle_ebadf(self.0.read_to_end(buf), 0)
     }
@@ -514,10 +509,6 @@ impl Read for Stdin {
     fn is_read_vectored(&self) -> bool {
         self.lock().is_read_vectored()
     }
-    #[inline]
-    unsafe fn initializer(&self) -> Initializer {
-        Initializer::nop()
-    }
     fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
         self.lock().read_to_end(buf)
     }
@@ -552,11 +543,6 @@ impl Read for StdinLock<'_> {
         self.inner.is_read_vectored()
     }
 
-    #[inline]
-    unsafe fn initializer(&self) -> Initializer {
-        Initializer::nop()
-    }
-
     fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
         self.inner.read_to_end(buf)
     }
diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs
index 0321a2b60b108..ea49bfe3421d1 100644
--- a/library/std/src/io/tests.rs
+++ b/library/std/src/io/tests.rs
@@ -1,7 +1,8 @@
-use super::{repeat, Cursor, SeekFrom};
+use super::{repeat, Cursor, ReadBuf, SeekFrom};
 use crate::cmp::{self, min};
 use crate::io::{self, IoSlice, IoSliceMut};
 use crate::io::{BufRead, BufReader, Read, Seek, Write};
+use crate::mem::MaybeUninit;
 use crate::ops::Deref;
 
 #[test]
@@ -156,6 +157,28 @@ fn read_exact_slice() {
     assert_eq!(c, b"9");
 }
 
+#[test]
+fn read_buf_exact() {
+    let mut buf = [0; 4];
+    let mut buf = ReadBuf::new(&mut buf);
+
+    let mut c = Cursor::new(&b""[..]);
+    assert_eq!(c.read_buf_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
+
+    let mut c = Cursor::new(&b"123456789"[..]);
+    c.read_buf_exact(&mut buf).unwrap();
+    assert_eq!(buf.filled(), b"1234");
+
+    buf.clear();
+
+    c.read_buf_exact(&mut buf).unwrap();
+    assert_eq!(buf.filled(), b"5678");
+
+    buf.clear();
+
+    assert_eq!(c.read_buf_exact(&mut buf).unwrap_err().kind(), io::ErrorKind::UnexpectedEof);
+}
+
 #[test]
 fn take_eof() {
     struct R;
@@ -559,3 +582,23 @@ fn test_write_all_vectored() {
         }
     }
 }
+
+#[bench]
+fn bench_take_read(b: &mut test::Bencher) {
+    b.iter(|| {
+        let mut buf = [0; 64];
+
+        [255; 128].take(64).read(&mut buf).unwrap();
+    });
+}
+
+#[bench]
+fn bench_take_read_buf(b: &mut test::Bencher) {
+    b.iter(|| {
+        let mut buf = [MaybeUninit::uninit(); 64];
+
+        let mut rbuf = ReadBuf::uninit(&mut buf);
+
+        [255; 128].take(64).read_buf(&mut rbuf).unwrap();
+    });
+}
diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs
index 9cd7c5148499d..c1300cd67c086 100644
--- a/library/std/src/io/util.rs
+++ b/library/std/src/io/util.rs
@@ -5,7 +5,7 @@ mod tests;
 
 use crate::fmt;
 use crate::io::{
-    self, BufRead, Initializer, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write,
+    self, BufRead, IoSlice, IoSliceMut, Read, ReadBuf, Seek, SeekFrom, SizeHint, Write,
 };
 
 /// A reader which is always at EOF.
@@ -47,8 +47,8 @@ impl Read for Empty {
     }
 
     #[inline]
-    unsafe fn initializer(&self) -> Initializer {
-        Initializer::nop()
+    fn read_buf(&mut self, _buf: &mut ReadBuf<'_>) -> io::Result<()> {
+        Ok(())
     }
 }
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -130,6 +130,24 @@ impl Read for Repeat {
         Ok(buf.len())
     }
 
+    fn read_buf(&mut self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+        // SAFETY: No uninit bytes are being written
+        for slot in unsafe { buf.unfilled_mut() } {
+            slot.write(self.byte);
+        }
+
+        let remaining = buf.remaining();
+
+        // SAFETY: the entire unfilled portion of buf has been initialized
+        unsafe {
+            buf.assume_init(remaining);
+        }
+
+        buf.add_filled(remaining);
+
+        Ok(())
+    }
+
     #[inline]
     fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
         let mut nwritten = 0;
@@ -143,11 +161,6 @@ impl Read for Repeat {
     fn is_read_vectored(&self) -> bool {
         true
     }
-
-    #[inline]
-    unsafe fn initializer(&self) -> Initializer {
-        Initializer::nop()
-    }
 }
 
 impl SizeHint for Repeat {
diff --git a/library/std/src/io/util/tests.rs b/library/std/src/io/util/tests.rs
index 7632eaf872a5b..08972a59a833f 100644
--- a/library/std/src/io/util/tests.rs
+++ b/library/std/src/io/util/tests.rs
@@ -1,9 +1,12 @@
 use crate::cmp::{max, min};
 use crate::io::prelude::*;
 use crate::io::{
-    copy, empty, repeat, sink, BufWriter, Empty, Repeat, Result, SeekFrom, Sink, DEFAULT_BUF_SIZE,
+    copy, empty, repeat, sink, BufWriter, Empty, ReadBuf, Repeat, Result, SeekFrom, Sink,
+    DEFAULT_BUF_SIZE,
 };
 
+use crate::mem::MaybeUninit;
+
 #[test]
 fn copy_copies() {
     let mut r = repeat(0).take(4);
@@ -75,6 +78,30 @@ fn empty_reads() {
     assert_eq!(e.read(&mut [0]).unwrap(), 0);
     assert_eq!(e.read(&mut [0; 1024]).unwrap(), 0);
     assert_eq!(e.by_ref().read(&mut [0; 1024]).unwrap(), 0);
+
+    let mut buf = [];
+    let mut buf = ReadBuf::uninit(&mut buf);
+    e.read_buf(&mut buf).unwrap();
+    assert_eq!(buf.filled_len(), 0);
+    assert_eq!(buf.initialized_len(), 0);
+
+    let mut buf = [MaybeUninit::uninit()];
+    let mut buf = ReadBuf::uninit(&mut buf);
+    e.read_buf(&mut buf).unwrap();
+    assert_eq!(buf.filled_len(), 0);
+    assert_eq!(buf.initialized_len(), 0);
+
+    let mut buf = [MaybeUninit::uninit(); 1024];
+    let mut buf = ReadBuf::uninit(&mut buf);
+    e.read_buf(&mut buf).unwrap();
+    assert_eq!(buf.filled_len(), 0);
+    assert_eq!(buf.initialized_len(), 0);
+
+    let mut buf = [MaybeUninit::uninit(); 1024];
+    let mut buf = ReadBuf::uninit(&mut buf);
+    e.by_ref().read_buf(&mut buf).unwrap();
+    assert_eq!(buf.filled_len(), 0);
+    assert_eq!(buf.initialized_len(), 0);
 }
 
 #[test]
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index c2243b259538a..dd55ba5731740 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -306,6 +306,7 @@
 #![feature(maybe_uninit_extra)]
 #![feature(maybe_uninit_slice)]
 #![feature(maybe_uninit_uninit_array)]
+#![feature(maybe_uninit_write_slice)]
 #![feature(min_specialization)]
 #![feature(mixed_integer_ops)]
 #![feature(must_not_suspend)]
@@ -321,6 +322,7 @@
 #![feature(panic_unwind)]
 #![feature(pin_static_ref)]
 #![feature(prelude_import)]
+#![feature(ptr_as_uninit)]
 #![feature(ptr_internals)]
 #![feature(rustc_attrs)]
 #![feature(rustc_private)]
diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs
index 5738862fb58b2..1ba54d892e3ce 100644
--- a/library/std/src/net/tcp.rs
+++ b/library/std/src/net/tcp.rs
@@ -6,7 +6,7 @@ mod tests;
 use crate::io::prelude::*;
 
 use crate::fmt;
-use crate::io::{self, Initializer, IoSlice, IoSliceMut};
+use crate::io::{self, IoSlice, IoSliceMut};
 use crate::net::{Shutdown, SocketAddr, ToSocketAddrs};
 use crate::sys_common::net as net_imp;
 use crate::sys_common::{AsInner, FromInner, IntoInner};
@@ -626,12 +626,6 @@ impl Read for TcpStream {
     fn is_read_vectored(&self) -> bool {
         self.0.is_read_vectored()
     }
-
-    #[inline]
-    unsafe fn initializer(&self) -> Initializer {
-        // SAFETY: Read is guaranteed to work on uninitialized memory
-        unsafe { Initializer::nop() }
-    }
 }
 #[stable(feature = "rust1", since = "1.0.0")]
 impl Write for TcpStream {
@@ -666,12 +660,6 @@ impl Read for &TcpStream {
     fn is_read_vectored(&self) -> bool {
         self.0.is_read_vectored()
     }
-
-    #[inline]
-    unsafe fn initializer(&self) -> Initializer {
-        // SAFETY: Read is guaranteed to work on uninitialized memory
-        unsafe { Initializer::nop() }
-    }
 }
 #[stable(feature = "rust1", since = "1.0.0")]
 impl Write for &TcpStream {
diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs
index 6120d557227af..583f861a92535 100644
--- a/library/std/src/os/unix/net/stream.rs
+++ b/library/std/src/os/unix/net/stream.rs
@@ -11,7 +11,7 @@
 use super::{recv_vectored_with_ancillary_from, send_vectored_with_ancillary_to, SocketAncillary};
 use super::{sockaddr_un, SocketAddr};
 use crate::fmt;
-use crate::io::{self, Initializer, IoSlice, IoSliceMut};
+use crate::io::{self, IoSlice, IoSliceMut};
 use crate::net::Shutdown;
 use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
 #[cfg(any(
@@ -624,11 +624,6 @@ impl io::Read for UnixStream {
     fn is_read_vectored(&self) -> bool {
         io::Read::is_read_vectored(&&*self)
     }
-
-    #[inline]
-    unsafe fn initializer(&self) -> Initializer {
-        Initializer::nop()
-    }
 }
 
 #[stable(feature = "unix_socket", since = "1.10.0")]
@@ -645,11 +640,6 @@ impl<'a> io::Read for &'a UnixStream {
     fn is_read_vectored(&self) -> bool {
         self.0.is_read_vectored()
     }
-
-    #[inline]
-    unsafe fn initializer(&self) -> Initializer {
-        Initializer::nop()
-    }
 }
 
 #[stable(feature = "unix_socket", since = "1.10.0")]
diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index 9cc7fc2f0352e..2e51d28b104d4 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -109,7 +109,7 @@ use crate::io::prelude::*;
 use crate::ffi::OsStr;
 use crate::fmt;
 use crate::fs;
-use crate::io::{self, Initializer, IoSlice, IoSliceMut};
+use crate::io::{self, IoSlice, IoSliceMut};
 use crate::num::NonZeroI32;
 use crate::path::Path;
 use crate::str;
@@ -361,12 +361,6 @@ impl Read for ChildStdout {
     fn is_read_vectored(&self) -> bool {
         self.inner.is_read_vectored()
     }
-
-    #[inline]
-    unsafe fn initializer(&self) -> Initializer {
-        // SAFETY: Read is guaranteed to work on uninitialized memory
-        unsafe { Initializer::nop() }
-    }
 }
 
 impl AsInner<AnonPipe> for ChildStdout {
@@ -428,12 +422,6 @@ impl Read for ChildStderr {
     fn is_read_vectored(&self) -> bool {
         self.inner.is_read_vectored()
     }
-
-    #[inline]
-    unsafe fn initializer(&self) -> Initializer {
-        // SAFETY: Read is guaranteed to work on uninitialized memory
-        unsafe { Initializer::nop() }
-    }
 }
 
 impl AsInner<AnonPipe> for ChildStderr {
diff --git a/library/std/src/sys/hermit/fd.rs b/library/std/src/sys/hermit/fd.rs
index c400f5f2c2e84..1179a49c22fd0 100644
--- a/library/std/src/sys/hermit/fd.rs
+++ b/library/std/src/sys/hermit/fd.rs
@@ -1,6 +1,6 @@
 #![unstable(reason = "not public", issue = "none", feature = "fd")]
 
-use crate::io::{self, Read};
+use crate::io::{self, Read, ReadBuf};
 use crate::mem;
 use crate::sys::cvt;
 use crate::sys::hermit::abi;
diff --git a/library/std/src/sys/hermit/fs.rs b/library/std/src/sys/hermit/fs.rs
index be019d4435db5..974c44eb8dd5e 100644
--- a/library/std/src/sys/hermit/fs.rs
+++ b/library/std/src/sys/hermit/fs.rs
@@ -2,7 +2,7 @@ use crate::ffi::{CStr, CString, OsString};
 use crate::fmt;
 use crate::hash::{Hash, Hasher};
 use crate::io::{self, Error, ErrorKind};
-use crate::io::{IoSlice, IoSliceMut, SeekFrom};
+use crate::io::{IoSlice, IoSliceMut, ReadBuf, SeekFrom};
 use crate::os::unix::ffi::OsStrExt;
 use crate::path::{Path, PathBuf};
 use crate::sys::cvt;
@@ -312,6 +312,10 @@ impl File {
         false
     }
 
+    pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+        crate::io::default_read_buf(|buf| self.read(buf), buf)
+    }
+
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
         self.0.write(buf)
     }
diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs
index 0956726084e02..93cdf89e1db30 100644
--- a/library/std/src/sys/unix/fd.rs
+++ b/library/std/src/sys/unix/fd.rs
@@ -4,7 +4,7 @@
 mod tests;
 
 use crate::cmp;
-use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read};
+use crate::io::{self, IoSlice, IoSliceMut, Read, ReadBuf};
 use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
 use crate::sys::cvt;
 use crate::sys_common::{AsInner, FromInner, IntoInner};
@@ -127,6 +127,23 @@ impl FileDesc {
         }
     }
 
+    pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+        let ret = cvt(unsafe {
+            libc::read(
+                self.as_raw_fd(),
+                buf.unfilled_mut().as_mut_ptr() as *mut c_void,
+                cmp::min(buf.remaining(), READ_LIMIT),
+            )
+        })?;
+
+        // Safety: `ret` bytes were written to the initialized portion of the buffer
+        unsafe {
+            buf.assume_init(ret as usize);
+        }
+        buf.add_filled(ret as usize);
+        Ok(())
+    }
+
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
         let ret = cvt(unsafe {
             libc::write(
@@ -289,11 +306,6 @@ impl<'a> Read for &'a FileDesc {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
         (**self).read(buf)
     }
-
-    #[inline]
-    unsafe fn initializer(&self) -> Initializer {
-        Initializer::nop()
-    }
 }
 
 impl AsInner<OwnedFd> for FileDesc {
diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs
index a4fff9b2e6473..45eac987aa4cc 100644
--- a/library/std/src/sys/unix/fs.rs
+++ b/library/std/src/sys/unix/fs.rs
@@ -2,7 +2,7 @@ use crate::os::unix::prelude::*;
 
 use crate::ffi::{CStr, CString, OsStr, OsString};
 use crate::fmt;
-use crate::io::{self, Error, IoSlice, IoSliceMut, SeekFrom};
+use crate::io::{self, Error, IoSlice, IoSliceMut, ReadBuf, SeekFrom};
 use crate::mem;
 use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd};
 use crate::path::{Path, PathBuf};
@@ -864,6 +864,10 @@ impl File {
         self.0.read_at(buf, offset)
     }
 
+    pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+        self.0.read_buf(buf)
+    }
+
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
         self.0.write(buf)
     }
diff --git a/library/std/src/sys/unsupported/fs.rs b/library/std/src/sys/unsupported/fs.rs
index 6b45e29c1458e..d1d2847cd33be 100644
--- a/library/std/src/sys/unsupported/fs.rs
+++ b/library/std/src/sys/unsupported/fs.rs
@@ -1,7 +1,7 @@
 use crate::ffi::OsString;
 use crate::fmt;
 use crate::hash::{Hash, Hasher};
-use crate::io::{self, IoSlice, IoSliceMut, SeekFrom};
+use crate::io::{self, IoSlice, IoSliceMut, ReadBuf, SeekFrom};
 use crate::path::{Path, PathBuf};
 use crate::sys::time::SystemTime;
 use crate::sys::unsupported;
@@ -206,6 +206,10 @@ impl File {
         self.0
     }
 
+    pub fn read_buf(&self, _buf: &mut ReadBuf<'_>) -> io::Result<()> {
+        self.0
+    }
+
     pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
         self.0
     }
diff --git a/library/std/src/sys/wasi/fs.rs b/library/std/src/sys/wasi/fs.rs
index 984dda8dc0b4e..1a3da3746ac61 100644
--- a/library/std/src/sys/wasi/fs.rs
+++ b/library/std/src/sys/wasi/fs.rs
@@ -3,7 +3,7 @@
 use super::fd::WasiFd;
 use crate::ffi::{CStr, CString, OsStr, OsString};
 use crate::fmt;
-use crate::io::{self, IoSlice, IoSliceMut, SeekFrom};
+use crate::io::{self, IoSlice, IoSliceMut, ReadBuf, SeekFrom};
 use crate::iter;
 use crate::mem::{self, ManuallyDrop};
 use crate::os::raw::c_int;
@@ -411,6 +411,10 @@ impl File {
         true
     }
 
+    pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+        crate::io::default_read_buf(|buf| self.read(buf), buf)
+    }
+
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
         self.write_vectored(&[IoSlice::new(buf)])
     }
diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs
index 9859000c8d417..b258fc0478bdc 100644
--- a/library/std/src/sys/windows/fs.rs
+++ b/library/std/src/sys/windows/fs.rs
@@ -2,7 +2,7 @@ use crate::os::windows::prelude::*;
 
 use crate::ffi::OsString;
 use crate::fmt;
-use crate::io::{self, Error, IoSlice, IoSliceMut, SeekFrom};
+use crate::io::{self, Error, IoSlice, IoSliceMut, ReadBuf, SeekFrom};
 use crate::mem;
 use crate::os::windows::io::{AsHandle, BorrowedHandle};
 use crate::path::{Path, PathBuf};
@@ -420,6 +420,10 @@ impl File {
         self.handle.read_at(buf, offset)
     }
 
+    pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+        self.handle.read_buf(buf)
+    }
+
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
         self.handle.write(buf)
     }
diff --git a/library/std/src/sys/windows/handle.rs b/library/std/src/sys/windows/handle.rs
index 21d86b002264a..c3a3482f91051 100644
--- a/library/std/src/sys/windows/handle.rs
+++ b/library/std/src/sys/windows/handle.rs
@@ -1,7 +1,7 @@
 #![unstable(issue = "none", feature = "windows_handle")]
 
 use crate::cmp;
-use crate::io::{self, ErrorKind, IoSlice, IoSliceMut, Read};
+use crate::io::{self, ErrorKind, IoSlice, IoSliceMut, Read, ReadBuf};
 use crate::mem;
 use crate::os::windows::io::{
     AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle,
@@ -130,6 +130,39 @@ impl Handle {
         }
     }
 
+    pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+        let mut read = 0;
+        let len = cmp::min(buf.remaining(), <c::DWORD>::MAX as usize) as c::DWORD;
+        let res = cvt(unsafe {
+            c::ReadFile(
+                self.as_raw_handle(),
+                buf.unfilled_mut().as_mut_ptr() as c::LPVOID,
+                len,
+                &mut read,
+                ptr::null_mut(),
+            )
+        });
+
+        match res {
+            Ok(_) => {
+                // Safety: `read` bytes were written to the initialized portion of the buffer
+                unsafe {
+                    buf.assume_init(read as usize);
+                }
+                buf.add_filled(read as usize);
+                Ok(())
+            }
+
+            // The special treatment of BrokenPipe is to deal with Windows
+            // pipe semantics, which yields this error when *reading* from
+            // a pipe after the other end has closed; we interpret that as
+            // EOF on the pipe.
+            Err(ref e) if e.kind() == ErrorKind::BrokenPipe => Ok(()),
+
+            Err(e) => Err(e),
+        }
+    }
+
     pub unsafe fn read_overlapped(
         &self,
         buf: &mut [u8],