Repository: tokio-rs/tokio
File: tokio/src/sync/watch.rs (Rust)

Code before:
    ///
    /// Outstanding borrows hold a read lock. This means that long lived borrows
    /// could cause the send half to block. It is recommended to keep the borrow
    /// as short lived as possible.
    ///
    /// The priority policy of the lock is dependent on the underlying lock
    /// implementation, and this type does not guarantee that any particular policy
    /// will be used. In particular, a producer which is waiting to acquire the lock
    /// in `send` might or might not block concurrent calls to `borrow`, e.g.:
    ///
    /// <details><summary>Potential deadlock example</summary>
    ///
    /// ```text
    /// // Task 1 (on thread A)                       |  // Task 2 (on thread B)
    /// let (_ref1, _updated1) =                      |
    ///         rx1.borrow_and_update_if_changed();   |
    ///                                               |  // will block
    ///                                               |  let _ = tx.send(());
    /// // may deadlock                               |
    /// let (_ref2, _updated2) =                      |
    ///         rx2.borrow_and_update_if_changed();   |
    /// ```
    /// </details>
    ///
    /// [`changed`]: Receiver::changed
    pub fn borrow_and_update_if_changed(&mut self) -> (Ref<'_, T>, bool) {
        let inner = self.shared.value.read().unwrap();
        // After obtaining a read-lock no concurrent writes could occur
        // and the loaded version matches that of the borrowed reference.
        let new_version = self.shared.state.load().version();
        let updated = if self.version != new_version {
            self.version = new_version;
            true
        } else {
            false
        };
        (Ref { inner }, updated)
    }

    /// Checks if this channel contains a message that this receiver has not yet
    /// seen. The new value is not marked as seen.
    ///
    /// Although this method is called `has_changed`, it does not check new
    /// messages for equality, so this call will return true even if the new
    /// message is equal to the old message.
    ///
    /// Returns an error if the channel has been closed.
    /// # Examples
    ///
    /// ```
    /// use tokio::sync::watch;

Review comment (suggestion):
here's a minor API design suggestion. what if, instead of a tuple of `Ref` and `bool`, we added the bool to `ref` along with a `Ref::changed()` method. that way, the meaning of the boolean value would be clearer at the callsite, as opposed to making the user responsible for naming the tuple field. it would also negate the need to have two separate methods.

Code after:
    /// [`changed`]: Receiver::changed
    ///
    /// # Examples
    ///
    /// ```
    /// use tokio::sync::watch;
    ///
    /// let (_, rx) = watch::channel("hello");
    /// assert_eq!(*rx.borrow(), "hello");
    /// ```
    pub fn borrow(&self) -> Ref<'_, T> {
        let inner = self.shared.value.read().unwrap();

        // After obtaining a read-lock no concurrent writes could occur
        // and the loaded version matches that of the borrowed reference.
        let new_version = self.shared.state.load().version();
        let has_changed = self.version != new_version;

        Ref { inner, has_changed }
    }

    /// Returns a reference to the most recently sent value and marks that value
    /// as seen.
    ///
    /// This method marks the current value as seen. Subsequent calls to [`changed`]
    /// will not return immediately until the [`Sender`] has modified the shared
    /// value again.
    ///
    /// Outstanding borrows hold a read lock. This means that long lived borrows
    /// could cause the send half to block. It is recommended to keep the borrow
    /// as short lived as possible. Additionally, if you are running in an
    /// environment that allows `!Send` futures, you must ensure that the
    /// returned `Ref` type is never held alive across an `.await` point.
    ///
    /// The priority policy of the lock is dependent on the underlying lock
    /// implementation, and this type does not guarantee that any particular policy
    /// will be used. In particular, a producer which is waiting to acquire the lock
    /// in `send` might or might not block concurrent calls to `borrow`, e.g.:
    ///
    /// <details><summary>Potential deadlock example</summary>
    ///
    /// ```text
    /// // Task 1 (on thread A)                |  // Task 2 (on thread B)
    /// let _ref1 = rx1.borrow_and_update();   |
    ///                                        |  // will block
    ///                                        |  let _ = tx.send(());
    /// // may deadlock                        |
    /// let _ref2 = rx2.borrow_and_update();   |
    /// ```
    /// </details>
    ///