diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs
index cb3f864fd4e1e..17178b4a64147 100644
--- a/library/std/src/io/util.rs
+++ b/library/std/src/io/util.rs
@@ -68,6 +68,38 @@ impl Read for Empty {
     fn read_buf(&mut self, _cursor: BorrowedCursor<'_>) -> io::Result<()> {
         Ok(())
     }
+
+    #[inline]
+    fn read_vectored(&mut self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+        Ok(0)
+    }
+
+    #[inline]
+    fn is_read_vectored(&self) -> bool {
+        // Do not force `Chain<Empty, T>` or `Chain<T, Empty>` to use vectored
+        // reads, unless the other reader is vectored.
+        false
+    }
+
+    #[inline]
+    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
+        if !buf.is_empty() { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) }
+    }
+
+    #[inline]
+    fn read_buf_exact(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
+        if cursor.capacity() != 0 { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) }
+    }
+
+    #[inline]
+    fn read_to_end(&mut self, _buf: &mut Vec<u8>) -> io::Result<usize> {
+        Ok(0)
+    }
+
+    #[inline]
+    fn read_to_string(&mut self, _buf: &mut String) -> io::Result<usize> {
+        Ok(0)
+    }
 }
 #[stable(feature = "rust1", since = "1.0.0")]
 impl BufRead for Empty {
@@ -75,20 +107,44 @@ impl BufRead for Empty {
     fn fill_buf(&mut self) -> io::Result<&[u8]> {
         Ok(&[])
     }
+
     #[inline]
     fn consume(&mut self, _n: usize) {}
+
+    #[inline]
+    fn has_data_left(&mut self) -> io::Result<bool> {
+        Ok(false)
+    }
+
+    #[inline]
+    fn read_until(&mut self, _byte: u8, _buf: &mut Vec<u8>) -> io::Result<usize> {
+        Ok(0)
+    }
+
+    #[inline]
+    fn skip_until(&mut self, _byte: u8) -> io::Result<usize> {
+        Ok(0)
+    }
+
+    #[inline]
+    fn read_line(&mut self, _buf: &mut String) -> io::Result<usize> {
+        Ok(0)
+    }
 }
 
 #[stable(feature = "empty_seek", since = "1.51.0")]
 impl Seek for Empty {
+    #[inline]
     fn seek(&mut self, _pos: SeekFrom) -> io::Result<u64> {
         Ok(0)
     }
 
+    #[inline]
     fn stream_len(&mut self) -> io::Result<u64> {
         Ok(0)
     }
 
+    #[inline]
     fn stream_position(&mut self) -> io::Result<u64> {
         Ok(0)
     }
@@ -119,6 +175,21 @@ impl Write for Empty {
         true
     }
 
+    #[inline]
+    fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> {
+        Ok(())
+    }
+
+    #[inline]
+    fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
+        Ok(())
+    }
+
+    #[inline]
+    fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> {
+        Ok(())
+    }
+
     #[inline]
     fn flush(&mut self) -> io::Result<()> {
         Ok(())
@@ -143,6 +214,21 @@ impl Write for &Empty {
         true
     }
 
+    #[inline]
+    fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> {
+        Ok(())
+    }
+
+    #[inline]
+    fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
+        Ok(())
+    }
+
+    #[inline]
+    fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> {
+        Ok(())
+    }
+
     #[inline]
     fn flush(&mut self) -> io::Result<()> {
         Ok(())
@@ -302,6 +388,21 @@ impl Write for Sink {
         true
     }
 
+    #[inline]
+    fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> {
+        Ok(())
+    }
+
+    #[inline]
+    fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
+        Ok(())
+    }
+
+    #[inline]
+    fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> {
+        Ok(())
+    }
+
     #[inline]
     fn flush(&mut self) -> io::Result<()> {
         Ok(())
@@ -326,6 +427,21 @@ impl Write for &Sink {
         true
     }
 
+    #[inline]
+    fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> {
+        Ok(())
+    }
+
+    #[inline]
+    fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> {
+        Ok(())
+    }
+
+    #[inline]
+    fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> {
+        Ok(())
+    }
+
     #[inline]
     fn flush(&mut self) -> io::Result<()> {
         Ok(())
diff --git a/library/std/src/io/util/tests.rs b/library/std/src/io/util/tests.rs
index 0599a881af179..d0f106d7af416 100644
--- a/library/std/src/io/util/tests.rs
+++ b/library/std/src/io/util/tests.rs
@@ -1,14 +1,51 @@
+use crate::fmt;
 use crate::io::prelude::*;
-use crate::io::{BorrowedBuf, Empty, Repeat, SeekFrom, Sink, empty, repeat, sink};
+use crate::io::{
+    BorrowedBuf, Empty, ErrorKind, IoSlice, IoSliceMut, Repeat, SeekFrom, Sink, empty, repeat, sink,
+};
 use crate::mem::MaybeUninit;
 
+struct ErrorDisplay;
+
+impl fmt::Display for ErrorDisplay {
+    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        Err(fmt::Error)
+    }
+}
+
+struct PanicDisplay;
+
+impl fmt::Display for PanicDisplay {
+    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        panic!()
+    }
+}
+
+#[track_caller]
+fn test_sinking<W: Write>(mut w: W) {
+    assert_eq!(w.write(&[]).unwrap(), 0);
+    assert_eq!(w.write(&[0]).unwrap(), 1);
+    assert_eq!(w.write(&[0; 1024]).unwrap(), 1024);
+    w.write_all(&[]).unwrap();
+    w.write_all(&[0]).unwrap();
+    w.write_all(&[0; 1024]).unwrap();
+    let mut bufs =
+        [IoSlice::new(&[]), IoSlice::new(&[0]), IoSlice::new(&[0; 1024]), IoSlice::new(&[])];
+    assert!(w.is_write_vectored());
+    assert_eq!(w.write_vectored(&[]).unwrap(), 0);
+    assert_eq!(w.write_vectored(&bufs).unwrap(), 1025);
+    w.write_all_vectored(&mut []).unwrap();
+    w.write_all_vectored(&mut bufs).unwrap();
+    assert!(w.flush().is_ok());
+    assert_eq!(w.by_ref().write(&[0; 1024]).unwrap(), 1024);
+    // Ignores fmt arguments
+    w.write_fmt(format_args!("{}", ErrorDisplay)).unwrap();
+    w.write_fmt(format_args!("{}", PanicDisplay)).unwrap();
+}
+
 #[test]
 fn sink_sinks() {
-    let mut s = sink();
-    assert_eq!(s.write(&[]).unwrap(), 0);
-    assert_eq!(s.write(&[0]).unwrap(), 1);
-    assert_eq!(s.write(&[0; 1024]).unwrap(), 1024);
-    assert_eq!(s.by_ref().write(&[0; 1024]).unwrap(), 1024);
+    test_sinking(sink());
 }
 
 #[test]
@@ -19,6 +56,21 @@ fn empty_reads() {
     assert_eq!(e.read(&mut [0; 1024]).unwrap(), 0);
     assert_eq!(Read::by_ref(&mut e).read(&mut [0; 1024]).unwrap(), 0);
 
+    e.read_exact(&mut []).unwrap();
+    assert_eq!(e.read_exact(&mut [0]).unwrap_err().kind(), ErrorKind::UnexpectedEof);
+    assert_eq!(e.read_exact(&mut [0; 1024]).unwrap_err().kind(), ErrorKind::UnexpectedEof);
+
+    assert!(!e.is_read_vectored());
+    assert_eq!(e.read_vectored(&mut []).unwrap(), 0);
+    let (mut buf1, mut buf1024) = ([0], [0; 1024]);
+    let bufs = &mut [
+        IoSliceMut::new(&mut []),
+        IoSliceMut::new(&mut buf1),
+        IoSliceMut::new(&mut buf1024),
+        IoSliceMut::new(&mut []),
+    ];
+    assert_eq!(e.read_vectored(bufs).unwrap(), 0);
+
     let buf: &mut [MaybeUninit<_>] = &mut [];
     let mut buf: BorrowedBuf<'_> = buf.into();
     e.read_buf(buf.unfilled()).unwrap();
@@ -42,6 +94,47 @@ fn empty_reads() {
     Read::by_ref(&mut e).read_buf(buf.unfilled()).unwrap();
     assert_eq!(buf.len(), 0);
     assert_eq!(buf.init_len(), 0);
+
+    let buf: &mut [MaybeUninit<_>] = &mut [];
+    let mut buf: BorrowedBuf<'_> = buf.into();
+    e.read_buf_exact(buf.unfilled()).unwrap();
+    assert_eq!(buf.len(), 0);
+    assert_eq!(buf.init_len(), 0);
+
+    let buf: &mut [_] = &mut [MaybeUninit::uninit()];
+    let mut buf: BorrowedBuf<'_> = buf.into();
+    assert_eq!(e.read_buf_exact(buf.unfilled()).unwrap_err().kind(), ErrorKind::UnexpectedEof);
+    assert_eq!(buf.len(), 0);
+    assert_eq!(buf.init_len(), 0);
+
+    let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024];
+    let mut buf: BorrowedBuf<'_> = buf.into();
+    assert_eq!(e.read_buf_exact(buf.unfilled()).unwrap_err().kind(), ErrorKind::UnexpectedEof);
+    assert_eq!(buf.len(), 0);
+    assert_eq!(buf.init_len(), 0);
+
+    let buf: &mut [_] = &mut [MaybeUninit::uninit(); 1024];
+    let mut buf: BorrowedBuf<'_> = buf.into();
+    assert_eq!(
+        Read::by_ref(&mut e).read_buf_exact(buf.unfilled()).unwrap_err().kind(),
+        ErrorKind::UnexpectedEof,
+    );
+    assert_eq!(buf.len(), 0);
+    assert_eq!(buf.init_len(), 0);
+
+    let mut buf = Vec::new();
+    assert_eq!(e.read_to_end(&mut buf).unwrap(), 0);
+    assert_eq!(buf, vec![]);
+    let mut buf = vec![1, 2, 3];
+    assert_eq!(e.read_to_end(&mut buf).unwrap(), 0);
+    assert_eq!(buf, vec![1, 2, 3]);
+
+    let mut buf = String::new();
+    assert_eq!(e.read_to_string(&mut buf).unwrap(), 0);
+    assert_eq!(buf, "");
+    let mut buf = "hello".to_owned();
+    assert_eq!(e.read_to_string(&mut buf).unwrap(), 0);
+    assert_eq!(buf, "hello");
 }
 
 #[test]
@@ -66,11 +159,7 @@ fn empty_seeks() {
 
 #[test]
 fn empty_sinks() {
-    let mut e = empty();
-    assert_eq!(e.write(&[]).unwrap(), 0);
-    assert_eq!(e.write(&[0]).unwrap(), 1);
-    assert_eq!(e.write(&[0; 1024]).unwrap(), 1024);
-    assert_eq!(Write::by_ref(&mut e).write(&[0; 1024]).unwrap(), 1024);
+    test_sinking(empty());
 }
 
 #[test]
diff --git a/tests/ui/write-fmt-errors.rs b/tests/ui/write-fmt-errors.rs
index 1dafb9a784b3a..b48fa3f11ccb1 100644
--- a/tests/ui/write-fmt-errors.rs
+++ b/tests/ui/write-fmt-errors.rs
@@ -4,7 +4,7 @@
 #![feature(io_error_uncategorized)]
 
 use std::fmt;
-use std::io::{self, Error, Write, sink};
+use std::io::{self, Error, Write};
 use std::panic::catch_unwind;
 
 struct ErrorDisplay;
@@ -33,7 +33,7 @@ fn main() {
     assert!(res.is_err(), "writer error did not propagate");
 
     // Test that the error from the formatter is detected.
-    let res = catch_unwind(|| write!(sink(), "{} {} {}", 1, ErrorDisplay, "bar"));
+    let res = catch_unwind(|| write!(vec![], "{} {} {}", 1, ErrorDisplay, "bar"));
     let err = res.expect_err("formatter error did not lead to panic").downcast::<&str>().unwrap();
     assert!(
         err.contains("formatting trait implementation returned an error"),