about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJake Goulding <jake.goulding@gmail.com>2022-07-21 21:39:20 -0400
committerJake Goulding <jake.goulding@gmail.com>2022-08-23 10:48:59 -0400
commit260ec9347838714ad9fa4f7e083bdc0448f4f9be (patch)
treef7ffc23cbc1e3ac507b57eaf838f05024d0f080b
parent38de102cffb7692738ec7940794e59452372d395 (diff)
downloadrust-260ec9347838714ad9fa4f7e083bdc0448f4f9be.tar.gz
rust-260ec9347838714ad9fa4f7e083bdc0448f4f9be.zip
Add `Provider::{would_be_satisfied_by_value_of,would_be_satisfied_by_ref_of}`
While the `provide_*` methods already short-circuit when a value has
been provided, there are times where an expensive computation is
needed to determine if the `provide_*` method can even be called.
-rw-r--r--library/core/src/any.rs165
1 files changed, 165 insertions, 0 deletions
diff --git a/library/core/src/any.rs b/library/core/src/any.rs
index 3bfb675531e..1a379ecc11c 100644
--- a/library/core/src/any.rs
+++ b/library/core/src/any.rs
@@ -1008,6 +1008,156 @@ impl<'a> Demand<'a> {
         }
         self
     }
+
+    /// Check if the `Demand` would be satisfied if provided with a
+    /// value of the specified type. If the type does not match or has
+    /// already been provided, returns false.
+    ///
+    /// # Examples
+    ///
+    /// Check if an `u8` still needs to be provided and then provides
+    /// it.
+    ///
+    /// ```rust
+    /// #![feature(provide_any)]
+    ///
+    /// use std::any::{Provider, Demand};
+    ///
+    /// struct Parent(Option<u8>);
+    ///
+    /// impl Provider for Parent {
+    ///     fn provide<'a>(&'a self, demand: &mut Demand<'a>) {
+    ///         if let Some(v) = self.0 {
+    ///             demand.provide_value::<u8>(v);
+    ///         }
+    ///     }
+    /// }
+    ///
+    /// struct Child {
+    ///     parent: Parent,
+    /// }
+    ///
+    /// impl Child {
+    ///     // Pretend that this takes a lot of resources to evaluate.
+    ///     fn an_expensive_computation(&self) -> Option<u8> {
+    ///         Some(99)
+    ///     }
+    /// }
+    ///
+    /// impl Provider for Child {
+    ///     fn provide<'a>(&'a self, demand: &mut Demand<'a>) {
+    ///         // In general, we don't know if this call will provide
+    ///         // an `u8` value or not...
+    ///         self.parent.provide(demand);
+    ///
+    ///         // ...so we check to see if the `u8` is needed before
+    ///         // we run our expensive computation.
+    ///         if demand.would_be_satisfied_by_value_of::<u8>() {
+    ///             if let Some(v) = self.an_expensive_computation() {
+    ///                 demand.provide_value::<u8>(v);
+    ///             }
+    ///         }
+    ///
+    ///         // The demand will be satisfied now, regardless of if
+    ///         // the parent provided the value or we did.
+    ///         assert!(!demand.would_be_satisfied_by_value_of::<u8>());
+    ///     }
+    /// }
+    ///
+    /// let parent = Parent(Some(42));
+    /// let child = Child { parent };
+    /// assert_eq!(Some(42), std::any::request_value::<u8>(&child));
+    ///
+    /// let parent = Parent(None);
+    /// let child = Child { parent };
+    /// assert_eq!(Some(99), std::any::request_value::<u8>(&child));
+    /// ```
+    #[unstable(feature = "provide_any", issue = "96024")]
+    pub fn would_be_satisfied_by_value_of<T>(&self) -> bool
+    where
+        T: 'static,
+    {
+        self.would_be_satisfied_by::<tags::Value<T>>()
+    }
+
+    /// Check if the `Demand` would be satisfied if provided with a
+    /// reference to a value of the specified type. If the type does
+    /// not match or has already been provided, returns false.
+    ///
+    /// # Examples
+    ///
+    /// Check if a `&str` still needs to be provided and then provides
+    /// it.
+    ///
+    /// ```rust
+    /// #![feature(provide_any)]
+    ///
+    /// use std::any::{Provider, Demand};
+    ///
+    /// struct Parent(Option<String>);
+    ///
+    /// impl Provider for Parent {
+    ///     fn provide<'a>(&'a self, demand: &mut Demand<'a>) {
+    ///         if let Some(v) = &self.0 {
+    ///             demand.provide_ref::<str>(v);
+    ///         }
+    ///     }
+    /// }
+    ///
+    /// struct Child {
+    ///     parent: Parent,
+    ///     name: String,
+    /// }
+    ///
+    /// impl Child {
+    ///     // Pretend that this takes a lot of resources to evaluate.
+    ///     fn an_expensive_computation(&self) -> Option<&str> {
+    ///         Some(&self.name)
+    ///     }
+    /// }
+    ///
+    /// impl Provider for Child {
+    ///     fn provide<'a>(&'a self, demand: &mut Demand<'a>) {
+    ///         // In general, we don't know if this call will provide
+    ///         // a `str` reference or not...
+    ///         self.parent.provide(demand);
+    ///
+    ///         // ...so we check to see if the `&str` is needed before
+    ///         // we run our expensive computation.
+    ///         if demand.would_be_satisfied_by_ref_of::<str>() {
+    ///             if let Some(v) = self.an_expensive_computation() {
+    ///                 demand.provide_ref::<str>(v);
+    ///             }
+    ///         }
+    ///
+    ///         // The demand will be satisfied now, regardless of if
+    ///         // the parent provided the reference or we did.
+    ///         assert!(!demand.would_be_satisfied_by_ref_of::<str>());
+    ///     }
+    /// }
+    ///
+    /// let parent = Parent(Some("parent".into()));
+    /// let child = Child { parent, name: "child".into() };
+    /// assert_eq!(Some("parent"), std::any::request_ref::<str>(&child));
+    ///
+    /// let parent = Parent(None);
+    /// let child = Child { parent, name: "child".into() };
+    /// assert_eq!(Some("child"), std::any::request_ref::<str>(&child));
+    /// ```
+    #[unstable(feature = "provide_any", issue = "96024")]
+    pub fn would_be_satisfied_by_ref_of<T>(&self) -> bool
+    where
+        T: ?Sized + 'static,
+    {
+        self.would_be_satisfied_by::<tags::Ref<tags::MaybeSizedValue<T>>>()
+    }
+
+    fn would_be_satisfied_by<I>(&self) -> bool
+    where
+        I: tags::Type<'a>,
+    {
+        matches!(self.0.downcast::<I>(), Some(TaggedOption(None)))
+    }
 }
 
 #[unstable(feature = "provide_any", issue = "96024")]
@@ -1112,6 +1262,21 @@ impl<'a> dyn Erased<'a> + 'a {
     /// Returns some reference to the dynamic value if it is tagged with `I`,
     /// or `None` otherwise.
     #[inline]
+    fn downcast<I>(&self) -> Option<&TaggedOption<'a, I>>
+    where
+        I: tags::Type<'a>,
+    {
+        if self.tag_id() == TypeId::of::<I>() {
+            // SAFETY: Just checked whether we're pointing to an I.
+            Some(unsafe { &*(self as *const Self).cast::<TaggedOption<'a, I>>() })
+        } else {
+            None
+        }
+    }
+
+    /// Returns some mutable reference to the dynamic value if it is tagged with `I`,
+    /// or `None` otherwise.
+    #[inline]
     fn downcast_mut<I>(&mut self) -> Option<&mut TaggedOption<'a, I>>
     where
         I: tags::Type<'a>,