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.
///
/// 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
/// 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 {
/// Attempt to return the contents of the internal buffer, filling it with more data
/// from the inner reader if it is empty.

View File

@ -5,9 +5,16 @@ use std::task::{Context, Poll};
/// 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`
/// 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 {
/// 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.
///
/// 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
/// 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:
///
/// * `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
/// underlying object.
///
/// This trait importantly means that the `write` method only works in the
/// context of a future's task. The object may panic if used outside of a task.
/// This trait importantly means that the [`write`][stdwrite] method only works in
/// 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
/// very similarly to the `write` method, notably that `Ok(())` means that the
/// Note that this trait also represents that the [`Write::flush`][stdflush] method
/// 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
/// current task is ready to receive a notification when flushing can make more
/// 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 {
/// Attempt to write bytes from `buf` into the object.
///

View File

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

View File

@ -9,13 +9,30 @@ use std::task::Poll;
cfg_io_std! {
/// A handle to the standard error stream of a process.
///
/// The handle implements the [`AsyncWrite`] trait, but beware that concurrent
/// writes to `Stderr` must be executed with care.
/// 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.
///
/// Created by the [`stderr`] function.
///
/// [`stderr`]: stderr()
/// [`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)]
pub struct 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
/// 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 {
let std = io::stderr();
Stderr {

View File

@ -13,7 +13,7 @@ cfg_io_std! {
/// reads of `Stdin` must be executed with care.
///
/// 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
/// available data is required, e.g. interactive use or when implementing a
/// 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
/// 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 {
let std = io::stdin();
Stdin {

View File

@ -9,13 +9,30 @@ use std::task::Poll;
cfg_io_std! {
/// A handle to the standard output stream of a process.
///
/// The handle implements the [`AsyncWrite`] trait, but beware that concurrent
/// writes to `Stdout` must be executed with care.
/// 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.
///
/// Created by the [`stdout`] function.
///
/// [`stdout`]: fn.stdout.html
/// [`AsyncWrite`]: trait.AsyncWrite.html
/// [`stdout`]: stdout()
/// [`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)]
pub struct 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.
///
/// The returned handle allows writing to standard out from the within the Tokio
/// runtime.
/// The returned handle allows writing to standard out from the within the
/// 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 {
let std = io::stdout();
Stdout {

View File

@ -5,7 +5,9 @@ use crate::io::util::split::{split, Split};
use crate::io::AsyncBufRead;
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 {
/// 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].
///
/// [std]: std::io::copy
///
/// # Errors
///
/// The returned future will finish with an error will return an error
@ -56,8 +58,6 @@ cfg_io_util! {
/// # Ok(())
/// # }
/// ```
///
/// [std]: std::io::copy
pub fn copy<'a, R, W>(reader: &'a mut R, writer: &'a mut W) -> Copy<'a, R, W>
where
R: AsyncRead + Unpin + ?Sized,

View File

@ -6,7 +6,7 @@ use std::pin::Pin;
use std::task::{Context, Poll};
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
/// 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].
///
/// [`empty`]: fn.empty.html
/// [std]: https://doc.rust-lang.org/std/io/struct.Empty.html
/// [std]: std::io::empty
pub struct Empty {
_p: (),
}
@ -25,20 +25,22 @@ cfg_io_util! {
///
/// This is an asynchronous version of [`std::io::empty`][std].
///
/// [std]: std::io::empty
///
/// # Examples
///
/// 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 {
Empty { _p: () }
}

View File

@ -14,7 +14,7 @@ cfg_io_util! {
/// This is an asynchronous version of [`std::io::Repeat`][std].
///
/// [repeat]: fn.repeat.html
/// [std]: https://doc.rust-lang.org/std/io/struct.Repeat.html
/// [std]: std::io::Repeat
#[derive(Debug)]
pub struct Repeat {
byte: u8,
@ -27,18 +27,20 @@ cfg_io_util! {
///
/// This is an asynchronous version of [`std::io::repeat`][std].
///
/// [std]: std::io::repeat
///
/// # Examples
///
/// ```
/// # 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]);
/// # }
/// ```
/// use tokio::io::{self, AsyncReadExt};
///
/// [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 {
Repeat { byte }
}

View File

@ -11,9 +11,10 @@ cfg_io_util! {
/// This struct is generally created by calling [`sink`][sink]. Please
/// 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 {
_p: (),
}
@ -21,21 +22,27 @@ cfg_io_util! {
/// Creates an instance of an async writer which will successfully consume all
/// 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
/// 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
///
/// ```rust
/// # use tokio::io::{self, AsyncWriteExt};
/// # async fn dox() {
/// let buffer = vec![1, 2, 3, 5, 8];
/// let num_bytes = io::sink().write(&buffer).await.unwrap();
/// assert_eq!(num_bytes, 5);
/// # }
/// ```
/// use tokio::io::{self, AsyncWriteExt};
///
/// #[tokio::main]
/// async fn main() -> io::Result<()> {
/// 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 {
Sink { _p: () }