Repository: tokio-rs/tokio
File: tokio/src/io/async_fd.rs (Rust)

Code before:
    /// provides shared access to the inner IO resource when handling the
    /// [`AsyncFdReadyGuard`].
    #[allow(clippy::needless_lifetimes)] // The lifetime improves rustdoc rendering.
    pub async fn writable<'a>(&'a self) -> io::Result<AsyncFdReadyGuard<'a, T>> {
        self.readiness(Interest::WRITABLE).await
    }

    /// Waits for the file descriptor to become writable, returning a
    /// [`AsyncFdReadyMutGuard`] that must be dropped to resume write-readiness
    /// polling.
    ///
    /// This method takes `&mut self`, so it is possible to access the inner IO
    /// resource mutably when handling the [`AsyncFdReadyMutGuard`].
    #[allow(clippy::needless_lifetimes)] // The lifetime improves rustdoc rendering.
    pub async fn writable_mut<'a>(&'a mut self) -> io::Result<AsyncFdReadyMutGuard<'a, T>> {
        self.readiness_mut(Interest::WRITABLE).await
    }

    /// Reads or writes from the file descriptor using a user-provided IO operation.
    ///
    /// This method is useful to retry an operation that might block (by returning
    /// a [`WouldBlock`] error) until it doesn't block any more.
    ///
    /// The readiness of the file descriptor is awaited and when the file descriptor is ready,
    /// the provided closure is called. The closure should attempt to perform
    /// IO operation on the file descriptor by manually calling the appropriate syscall.
    /// If the operation fails because the file descriptor is not actually ready,
    /// then the closure should return a [`WouldBlock`] error. In such case the
    /// readiness flag is cleared and the file descriptor readiness is awaited again.
    /// This loop is repeated until the closure returns an `Ok` or an error
    /// other than [`WouldBlock`].
    ///
    /// The closure should only return a [`WouldBlock`] error if it has performed
    /// an IO operation on the file descriptor that failed due to the file descriptor not being
    /// ready. Returning a [`WouldBlock`] error in any other situation will
    /// incorrectly clear the readiness flag, which can cause the file descriptor to
    /// behave incorrectly.
    ///
    /// The closure should not perform the IO operation using any of the methods
    /// defined on the Tokio [`AsyncFd`] type, as this will mess with the
    /// readiness flag and can cause the file descriptor to behave incorrectly.
    ///
    /// This method is not intended to be used with combined interests.
    /// The closure should perform only one type of IO operation, so it should not
    /// require more than one ready state. This method may panic or sleep forever
    /// if it is called with a combined interest.
    ///
    /// # Examples
    ///
    /// This example sends some bytes on the inner [`std::net::UdpSocket`]. [`async_io`] waits
    /// for readiness, and retries if the send operation does block. This example is equivalent

Review comment (suggestion):
I would like to, near the top of this doc, explicitly mention that this is a convenience method that does the same thing as waiting for readiness and using `try_io` in a loop.

Code after:
    /// provides shared access to the inner IO resource when handling the
    /// [`AsyncFdReadyGuard`].
    #[allow(clippy::needless_lifetimes)] // The lifetime improves rustdoc rendering.
    pub async fn writable<'a>(&'a self) -> io::Result<AsyncFdReadyGuard<'a, T>> {
        self.readiness(Interest::WRITABLE).await
    }

    /// Waits for the file descriptor to become writable, returning a
    /// [`AsyncFdReadyMutGuard`] that must be dropped to resume write-readiness
    /// polling.
    ///
    /// This method takes `&mut self`, so it is possible to access the inner IO
    /// resource mutably when handling the [`AsyncFdReadyMutGuard`].
    #[allow(clippy::needless_lifetimes)] // The lifetime improves rustdoc rendering.
    pub async fn writable_mut<'a>(&'a mut self) -> io::Result<AsyncFdReadyMutGuard<'a, T>> {
        self.readiness_mut(Interest::WRITABLE).await
    }

    /// Reads or writes from the file descriptor using a user-provided IO operation.
    ///
    /// The `async_io` method is a convenience utility that waits for the file
    /// descriptor to become ready, and then executes the provided IO operation.
    /// Since file descriptors may be marked ready spuriously, the closure will
    /// be called repeatedly until it returns something other than a
    /// [`WouldBlock`] error. This is done using the following loop:
    ///
    /// ```no_run
    /// # use std::io::{self, Result};
    /// # struct Dox<T> { inner: T }
    /// # impl<T> Dox<T> {
    /// #     async fn writable(&self) -> Result<&Self> {
    /// #         Ok(self)
    /// #     }
    /// #     fn try_io<R>(&self, _: impl FnMut(&T) -> Result<R>) -> Result<Result<R>> {
    /// #         panic!()
    /// #     }
    /// async fn async_io<R>(&self, mut f: impl FnMut(&T) -> io::Result<R>) -> io::Result<R> {
    ///     loop {
    ///         // or `readable` if called with the read interest.
    ///         let guard = self.writable().await?;
    ///
    ///         match guard.try_io(&mut f) {
    ///             Ok(result) => return result,
    ///             Err(_would_block) => continue,
    ///         }
    ///     }
    /// }
    /// # }
    /// ```
    ///
    /// The closure should only return a [`WouldBlock`] error if it has performed