docs: improve tokio::io API documentation (#2060)

* Links are added where missing and examples are improved.
* Improve `stdin`, `stdout`, and `stderr` documentation by going
  into more details regarding what can go wrong in concurrent
  situations and provide examples for `stdout` and `stderr`.
This commit is contained in:
Alice Ryhl 2020-01-07 18:17:01 +01:00 committed by Carl Lerche
parent 45da5f3510
commit 780d6f91a0
12 changed files with 174 additions and 55 deletions

View File

@ -7,9 +7,15 @@ use std::task::{Context, Poll};
/// Read bytes asynchronously. /// Read bytes asynchronously.
/// ///
/// This trait inherits from `std::io::BufRead` and indicates that an I/O object is /// This trait inherits from [`std::io::BufRead`] and indicates that an I/O object is
/// **non-blocking**. All non-blocking I/O objects must return an error when /// **non-blocking**. All non-blocking I/O objects must return an error when
/// bytes are unavailable instead of blocking the current thread. /// bytes are unavailable instead of blocking the current thread.
///
/// Utilities for working with `AsyncBufRead` values are provided by
/// [`AsyncBufReadExt`].
///
/// [`std::io::BufRead`]: std::io::BufRead
/// [`AsyncBufReadExt`]: crate::io::AsyncBufReadExt
pub trait AsyncBufRead: AsyncRead { pub trait AsyncBufRead: AsyncRead {
/// Attempt to return the contents of the internal buffer, filling it with more data /// Attempt to return the contents of the internal buffer, filling it with more data
/// from the inner reader if it is empty. /// from the inner reader if it is empty.

View File

@ -5,9 +5,16 @@ use std::task::{Context, Poll};
/// Seek bytes asynchronously. /// Seek bytes asynchronously.
/// ///
/// This trait is analogous to the `std::io::Seek` trait, but integrates /// This trait is analogous to the [`std::io::Seek`] trait, but integrates
/// with the asynchronous task system. In particular, the `start_seek` /// with the asynchronous task system. In particular, the `start_seek`
/// method, unlike `Seek::seek`, will not block the calling thread. /// method, unlike [`Seek::seek`], will not block the calling thread.
///
/// Utilities for working with `AsyncSeek` values are provided by
/// [`AsyncSeekExt`].
///
/// [`std::io::Seek`]: std::io::Seek
/// [`Seek::seek`]: std::io::Seek::seek()
/// [`AsyncSeekExt`]: crate::io::AsyncSeekExt
pub trait AsyncSeek { pub trait AsyncSeek {
/// Attempt to seek to an offset, in bytes, in a stream. /// Attempt to seek to an offset, in bytes, in a stream.
/// ///

View File

@ -6,11 +6,11 @@ use std::task::{Context, Poll};
/// Writes bytes asynchronously. /// Writes bytes asynchronously.
/// ///
/// The trait inherits from `std::io::Write` and indicates that an I/O object is /// The trait inherits from [`std::io::Write`] and indicates that an I/O object is
/// **nonblocking**. All non-blocking I/O objects must return an error when /// **nonblocking**. All non-blocking I/O objects must return an error when
/// bytes cannot be written instead of blocking the current thread. /// bytes cannot be written instead of blocking the current thread.
/// ///
/// Specifically, this means that the `poll_write` function will return one of /// Specifically, this means that the [`poll_write`] function will return one of
/// the following: /// the following:
/// ///
/// * `Poll::Ready(Ok(n))` means that `n` bytes of data was immediately /// * `Poll::Ready(Ok(n))` means that `n` bytes of data was immediately
@ -26,14 +26,23 @@ use std::task::{Context, Poll};
/// * `Poll::Ready(Err(e))` for other errors are standard I/O errors coming from the /// * `Poll::Ready(Err(e))` for other errors are standard I/O errors coming from the
/// underlying object. /// underlying object.
/// ///
/// This trait importantly means that the `write` method only works in the /// This trait importantly means that the [`write`][stdwrite] method only works in
/// context of a future's task. The object may panic if used outside of a task. /// the context of a future's task. The object may panic if used outside of a task.
/// ///
/// Note that this trait also represents that the `Write::flush` method works /// Note that this trait also represents that the [`Write::flush`][stdflush] method
/// very similarly to the `write` method, notably that `Ok(())` means that the /// works very similarly to the `write` method, notably that `Ok(())` means that the
/// writer has successfully been flushed, a "would block" error means that the /// writer has successfully been flushed, a "would block" error means that the
/// current task is ready to receive a notification when flushing can make more /// current task is ready to receive a notification when flushing can make more
/// progress, and otherwise normal errors can happen as well. /// progress, and otherwise normal errors can happen as well.
///
/// Utilities for working with `AsyncWrite` values are provided by
/// [`AsyncWriteExt`].
///
/// [`std::io::Write`]: std::io::Write
/// [`poll_write`]: AsyncWrite::poll_write()
/// [stdwrite]: std::io::Write::write()
/// [stdflush]: std::io::Write::flush()
/// [`AsyncWriteExt`]: crate::io::AsyncWriteExt
pub trait AsyncWrite { pub trait AsyncWrite {
/// Attempt to write bytes from `buf` into the object. /// Attempt to write bytes from `buf` into the object.
/// ///

View File

@ -17,12 +17,12 @@ use std::sync::Arc;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
cfg_io_util! { cfg_io_util! {
/// The readable half of a value returned from `split`. /// The readable half of a value returned from [`split`](split()).
pub struct ReadHalf<T> { pub struct ReadHalf<T> {
inner: Arc<Inner<T>>, inner: Arc<Inner<T>>,
} }
/// The writable half of a value returned from `split`. /// The writable half of a value returned from [`split`](split()).
pub struct WriteHalf<T> { pub struct WriteHalf<T> {
inner: Arc<Inner<T>>, inner: Arc<Inner<T>>,
} }
@ -30,8 +30,8 @@ cfg_io_util! {
/// Split a single value implementing `AsyncRead + AsyncWrite` into separate /// Split a single value implementing `AsyncRead + AsyncWrite` into separate
/// `AsyncRead` and `AsyncWrite` handles. /// `AsyncRead` and `AsyncWrite` handles.
/// ///
/// To restore this read/write object from its `split::ReadHalf` and /// To restore this read/write object from its `ReadHalf` and
/// `split::WriteHalf` use `unsplit`. /// `WriteHalf` use [`unsplit`](ReadHalf::unsplit()).
pub fn split<T>(stream: T) -> (ReadHalf<T>, WriteHalf<T>) pub fn split<T>(stream: T) -> (ReadHalf<T>, WriteHalf<T>)
where where
T: AsyncRead + AsyncWrite, T: AsyncRead + AsyncWrite,

View File

@ -9,13 +9,30 @@ use std::task::Poll;
cfg_io_std! { cfg_io_std! {
/// A handle to the standard error stream of a process. /// A handle to the standard error stream of a process.
/// ///
/// The handle implements the [`AsyncWrite`] trait, but beware that concurrent /// Concurrent writes to stderr must be executed with care: Only individual
/// writes to `Stderr` must be executed with care. /// writes to this [`AsyncWrite`] are guaranteed to be intact. In particular
/// you should be aware that writes using [`write_all`] are not guaranteed
/// to occur as a single write, so multiple threads writing data with
/// [`write_all`] may result in interleaved output.
/// ///
/// Created by the [`stderr`] function. /// Created by the [`stderr`] function.
/// ///
/// [`stderr`]: stderr() /// [`stderr`]: stderr()
/// [`AsyncWrite`]: AsyncWrite /// [`AsyncWrite`]: AsyncWrite
/// [`write_all`]: crate::io::AsyncWriteExt::write_all()
///
/// # Examples
///
/// ```
/// use tokio::io::{self, AsyncWriteExt};
///
/// #[tokio::main]
/// async fn main() -> io::Result<()> {
/// let mut stderr = io::stdout();
/// stderr.write_all(b"Print some error here.").await?;
/// Ok(())
/// }
/// ```
#[derive(Debug)] #[derive(Debug)]
pub struct Stderr { pub struct Stderr {
std: Blocking<std::io::Stderr>, std: Blocking<std::io::Stderr>,
@ -25,6 +42,28 @@ cfg_io_std! {
/// ///
/// The returned handle allows writing to standard error from the within the /// The returned handle allows writing to standard error from the within the
/// Tokio runtime. /// Tokio runtime.
///
/// Concurrent writes to stderr must be executed with care: Only individual
/// writes to this [`AsyncWrite`] are guaranteed to be intact. In particular
/// you should be aware that writes using [`write_all`] are not guaranteed
/// to occur as a single write, so multiple threads writing data with
/// [`write_all`] may result in interleaved output.
///
/// [`AsyncWrite`]: AsyncWrite
/// [`write_all`]: crate::io::AsyncWriteExt::write_all()
///
/// # Examples
///
/// ```
/// use tokio::io::{self, AsyncWriteExt};
///
/// #[tokio::main]
/// async fn main() -> io::Result<()> {
/// let mut stderr = io::stdout();
/// stderr.write_all(b"Print some error here.").await?;
/// Ok(())
/// }
/// ```
pub fn stderr() -> Stderr { pub fn stderr() -> Stderr {
let std = io::stderr(); let std = io::stderr();
Stderr { Stderr {

View File

@ -13,7 +13,7 @@ cfg_io_std! {
/// reads of `Stdin` must be executed with care. /// reads of `Stdin` must be executed with care.
/// ///
/// As an additional caveat, reading from the handle may block the calling /// As an additional caveat, reading from the handle may block the calling
/// future indefinitely, if there is not enough data available. This makes this /// future indefinitely if there is not enough data available. This makes this
/// handle unsuitable for use in any circumstance where immediate reaction to /// handle unsuitable for use in any circumstance where immediate reaction to
/// available data is required, e.g. interactive use or when implementing a /// available data is required, e.g. interactive use or when implementing a
/// subprocess driven by requests on the standard input. /// subprocess driven by requests on the standard input.
@ -31,6 +31,12 @@ cfg_io_std! {
/// ///
/// The returned handle allows reading from standard input from the within the /// The returned handle allows reading from standard input from the within the
/// Tokio runtime. /// Tokio runtime.
///
/// As an additional caveat, reading from the handle may block the calling
/// future indefinitely if there is not enough data available. This makes this
/// handle unsuitable for use in any circumstance where immediate reaction to
/// available data is required, e.g. interactive use or when implementing a
/// subprocess driven by requests on the standard input.
pub fn stdin() -> Stdin { pub fn stdin() -> Stdin {
let std = io::stdin(); let std = io::stdin();
Stdin { Stdin {

View File

@ -9,13 +9,30 @@ use std::task::Poll;
cfg_io_std! { cfg_io_std! {
/// A handle to the standard output stream of a process. /// A handle to the standard output stream of a process.
/// ///
/// The handle implements the [`AsyncWrite`] trait, but beware that concurrent /// Concurrent writes to stdout must be executed with care: Only individual
/// writes to `Stdout` must be executed with care. /// writes to this [`AsyncWrite`] are guaranteed to be intact. In particular
/// you should be aware that writes using [`write_all`] are not guaranteed
/// to occur as a single write, so multiple threads writing data with
/// [`write_all`] may result in interleaved output.
/// ///
/// Created by the [`stdout`] function. /// Created by the [`stdout`] function.
/// ///
/// [`stdout`]: fn.stdout.html /// [`stdout`]: stdout()
/// [`AsyncWrite`]: trait.AsyncWrite.html /// [`AsyncWrite`]: AsyncWrite
/// [`write_all`]: crate::io::AsyncWriteExt::write_all()
///
/// # Examples
///
/// ```
/// use tokio::io::{self, AsyncWriteExt};
///
/// #[tokio::main]
/// async fn main() -> io::Result<()> {
/// let mut stdout = io::stdout();
/// stdout.write_all(b"Hello world!").await?;
/// Ok(())
/// }
/// ```
#[derive(Debug)] #[derive(Debug)]
pub struct Stdout { pub struct Stdout {
std: Blocking<std::io::Stdout>, std: Blocking<std::io::Stdout>,
@ -23,8 +40,30 @@ cfg_io_std! {
/// Constructs a new handle to the standard output of the current process. /// Constructs a new handle to the standard output of the current process.
/// ///
/// The returned handle allows writing to standard out from the within the Tokio /// The returned handle allows writing to standard out from the within the
/// runtime. /// Tokio runtime.
///
/// Concurrent writes to stdout must be executed with care: Only individual
/// writes to this [`AsyncWrite`] are guaranteed to be intact. In particular
/// you should be aware that writes using [`write_all`] are not guaranteed
/// to occur as a single write, so multiple threads writing data with
/// [`write_all`] may result in interleaved output.
///
/// [`AsyncWrite`]: AsyncWrite
/// [`write_all`]: crate::io::AsyncWriteExt::write_all()
///
/// # Examples
///
/// ```
/// use tokio::io::{self, AsyncWriteExt};
///
/// #[tokio::main]
/// async fn main() -> io::Result<()> {
/// let mut stdout = io::stdout();
/// stdout.write_all(b"Hello world!").await?;
/// Ok(())
/// }
/// ```
pub fn stdout() -> Stdout { pub fn stdout() -> Stdout {
let std = io::stdout(); let std = io::stdout();
Stdout { Stdout {

View File

@ -5,7 +5,9 @@ use crate::io::util::split::{split, Split};
use crate::io::AsyncBufRead; use crate::io::AsyncBufRead;
cfg_io_util! { cfg_io_util! {
/// An extension trait which adds utility methods to `AsyncBufRead` types. /// An extension trait which adds utility methods to [`AsyncBufRead`] types.
///
/// [`AsyncBufRead`]: crate::io::AsyncBufRead
pub trait AsyncBufReadExt: AsyncBufRead { pub trait AsyncBufReadExt: AsyncBufRead {
/// Read all bytes into `buf` until the delimiter `byte` or EOF is reached. /// Read all bytes into `buf` until the delimiter `byte` or EOF is reached.
/// ///

View File

@ -36,6 +36,8 @@ cfg_io_util! {
/// ///
/// This is an asynchronous version of [`std::io::copy`][std]. /// This is an asynchronous version of [`std::io::copy`][std].
/// ///
/// [std]: std::io::copy
///
/// # Errors /// # Errors
/// ///
/// The returned future will finish with an error will return an error /// The returned future will finish with an error will return an error
@ -56,8 +58,6 @@ cfg_io_util! {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
///
/// [std]: std::io::copy
pub fn copy<'a, R, W>(reader: &'a mut R, writer: &'a mut W) -> Copy<'a, R, W> pub fn copy<'a, R, W>(reader: &'a mut R, writer: &'a mut W) -> Copy<'a, R, W>
where where
R: AsyncRead + Unpin + ?Sized, R: AsyncRead + Unpin + ?Sized,

View File

@ -6,7 +6,7 @@ use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
cfg_io_util! { cfg_io_util! {
// An async reader which is always at EOF. /// An async reader which is always at EOF.
/// ///
/// This struct is generally created by calling [`empty`]. Please see /// This struct is generally created by calling [`empty`]. Please see
/// the documentation of [`empty()`][`empty`] for more details. /// the documentation of [`empty()`][`empty`] for more details.
@ -14,7 +14,7 @@ cfg_io_util! {
/// This is an asynchronous version of [`std::io::empty`][std]. /// This is an asynchronous version of [`std::io::empty`][std].
/// ///
/// [`empty`]: fn.empty.html /// [`empty`]: fn.empty.html
/// [std]: https://doc.rust-lang.org/std/io/struct.Empty.html /// [std]: std::io::empty
pub struct Empty { pub struct Empty {
_p: (), _p: (),
} }
@ -25,20 +25,22 @@ cfg_io_util! {
/// ///
/// This is an asynchronous version of [`std::io::empty`][std]. /// This is an asynchronous version of [`std::io::empty`][std].
/// ///
/// [std]: std::io::empty
///
/// # Examples /// # Examples
/// ///
/// A slightly sad example of not reading anything into a buffer: /// A slightly sad example of not reading anything into a buffer:
/// ///
/// ```rust
/// # use tokio::io::{self, AsyncReadExt};
/// # async fn dox() {
/// let mut buffer = String::new();
/// io::empty().read_to_string(&mut buffer).await.unwrap();
/// assert!(buffer.is_empty());
/// # }
/// ``` /// ```
/// use tokio::io::{self, AsyncReadExt};
/// ///
/// [std]: https://doc.rust-lang.org/std/io/fn.empty.html /// #[tokio::main]
/// async fn main() {
/// let mut buffer = String::new();
/// io::empty().read_to_string(&mut buffer).await.unwrap();
/// assert!(buffer.is_empty());
/// }
/// ```
pub fn empty() -> Empty { pub fn empty() -> Empty {
Empty { _p: () } Empty { _p: () }
} }

View File

@ -14,7 +14,7 @@ cfg_io_util! {
/// This is an asynchronous version of [`std::io::Repeat`][std]. /// This is an asynchronous version of [`std::io::Repeat`][std].
/// ///
/// [repeat]: fn.repeat.html /// [repeat]: fn.repeat.html
/// [std]: https://doc.rust-lang.org/std/io/struct.Repeat.html /// [std]: std::io::Repeat
#[derive(Debug)] #[derive(Debug)]
pub struct Repeat { pub struct Repeat {
byte: u8, byte: u8,
@ -27,18 +27,20 @@ cfg_io_util! {
/// ///
/// This is an asynchronous version of [`std::io::repeat`][std]. /// This is an asynchronous version of [`std::io::repeat`][std].
/// ///
/// [std]: std::io::repeat
///
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// # use tokio::io::{self, AsyncReadExt}; /// use tokio::io::{self, AsyncReadExt};
/// # async fn dox() {
/// let mut buffer = [0; 3];
/// io::repeat(0b101).read_exact(&mut buffer).await.unwrap();
/// assert_eq!(buffer, [0b101, 0b101, 0b101]);
/// # }
/// ```
/// ///
/// [std]: https://doc.rust-lang.org/std/io/fn.repeat.html /// #[tokio::main]
/// async fn main() {
/// let mut buffer = [0; 3];
/// io::repeat(0b101).read_exact(&mut buffer).await.unwrap();
/// assert_eq!(buffer, [0b101, 0b101, 0b101]);
/// }
/// ```
pub fn repeat(byte: u8) -> Repeat { pub fn repeat(byte: u8) -> Repeat {
Repeat { byte } Repeat { byte }
} }

View File

@ -11,9 +11,10 @@ cfg_io_util! {
/// This struct is generally created by calling [`sink`][sink]. Please /// This struct is generally created by calling [`sink`][sink]. Please
/// see the documentation of `sink()` for more details. /// see the documentation of `sink()` for more details.
/// ///
/// This is an asynchronous version of `std::io::Sink`. /// This is an asynchronous version of [`std::io::Sink`][std].
/// ///
/// [sink]: fn.sink.html /// [sink]: sink()
/// [std]: std::io::Sink
pub struct Sink { pub struct Sink {
_p: (), _p: (),
} }
@ -21,21 +22,27 @@ cfg_io_util! {
/// Creates an instance of an async writer which will successfully consume all /// Creates an instance of an async writer which will successfully consume all
/// data. /// data.
/// ///
/// All calls to `poll_write` on the returned instance will return /// All calls to [`poll_write`] on the returned instance will return
/// `Poll::Ready(Ok(buf.len()))` and the contents of the buffer will not be /// `Poll::Ready(Ok(buf.len()))` and the contents of the buffer will not be
/// inspected. /// inspected.
/// ///
/// This is an asynchronous version of `std::io::sink`. /// This is an asynchronous version of [`std::io::sink`][std].
///
/// [`poll_write`]: crate::io::AsyncWrite::poll_write()
/// [std]: std::io::sink
/// ///
/// # Examples /// # Examples
/// ///
/// ```rust /// ```
/// # use tokio::io::{self, AsyncWriteExt}; /// use tokio::io::{self, AsyncWriteExt};
/// # async fn dox() { ///
/// let buffer = vec![1, 2, 3, 5, 8]; /// #[tokio::main]
/// let num_bytes = io::sink().write(&buffer).await.unwrap(); /// async fn main() -> io::Result<()> {
/// assert_eq!(num_bytes, 5); /// let buffer = vec![1, 2, 3, 5, 8];
/// # } /// let num_bytes = io::sink().write(&buffer).await?;
/// assert_eq!(num_bytes, 5);
/// Ok(())
/// }
/// ``` /// ```
pub fn sink() -> Sink { pub fn sink() -> Sink {
Sink { _p: () } Sink { _p: () }