diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 3b15ab1e6895b..5eda860264c6c 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -1,5 +1,9 @@ -//! This module implements the `Any` trait, which enables dynamic typing -//! of any `'static` type through runtime reflection. +//! This module contains the `Any` trait, which enables dynamic typing +//! of any `'static` type through runtime reflection. It also contains the +//! `Provider` trait and accompanying API, which enable trait objects to provide +//! data based on typed requests, an alternate form of runtime reflection. +//! +//! # `Any` and `TypeId` //! //! `Any` itself can be used to get a `TypeId`, and has more features when used //! as a trait object. As `&dyn Any` (a borrowed trait object), it has the `is` @@ -37,7 +41,7 @@ //! assert_eq!(boxed_id, TypeId::of::>()); //! ``` //! -//! # Examples +//! ## Examples //! //! Consider a situation where we want to log out a value passed to a function. //! We know the value we're working on implements Debug, but we don't know its @@ -81,6 +85,73 @@ //! do_work(&my_i8); //! } //! ``` +//! +//! # `Provider` and `Demand` +//! +//! `Provider` and the associated APIs support generic, type-driven access to data, and a mechanism +//! for implementers to provide such data. The key parts of the interface are the `Provider` +//! trait for objects which can provide data, and the [`request_value`] and [`request_ref`] +//! functions for requesting data from an object which implements `Provider`. Generally, end users +//! should not call `request_*` directly, they are helper functions for intermediate implementers +//! to use to implement a user-facing interface. This is purely for the sake of ergonomics, there is +//! safety concern here; intermediate implementers can typically support methods rather than +//! free functions and use more specific names. +//! +//! Typically, a data provider is a trait object of a trait which extends `Provider`. A user will +//! request data from a trait object by specifying the type of the data. +//! +//! ## Data flow +//! +//! * A user requests an object of a specific type, which is delegated to `request_value` or +//! `request_ref` +//! * `request_*` creates a `Demand` object and passes it to `Provider::provide` +//! * The data provider's implementation of `Provider::provide` tries providing values of +//! different types using `Demand::provide_*`. If the type matches the type requested by +//! the user, the value will be stored in the `Demand` object. +//! * `request_*` unpacks the `Demand` object and returns any stored value to the user. +//! +//! ## Examples +//! +//! ``` +//! # #![feature(provide_any)] +//! use std::any::{Provider, Demand, request_ref}; +//! +//! // Definition of MyTrait, a data provider. +//! trait MyTrait: Provider { +//! // ... +//! } +//! +//! // Methods on `MyTrait` trait objects. +//! impl dyn MyTrait + '_ { +//! /// Get a reference to a field of the implementing struct. +//! pub fn get_context_by_ref(&self) -> Option<&T> { +//! request_ref::(self) +//! } +//! } +//! +//! // Downstream implementation of `MyTrait` and `Provider`. +//! # struct SomeConcreteType { some_string: String } +//! impl MyTrait for SomeConcreteType { +//! // ... +//! } +//! +//! impl Provider for SomeConcreteType { +//! fn provide<'a>(&'a self, demand: &mut Demand<'a>) { +//! // Provide a string reference. We could provide multiple values with +//! // different types here. +//! demand.provide_ref::(&self.some_string); +//! } +//! } +//! +//! // Downstream usage of `MyTrait`. +//! fn use_my_trait(obj: &dyn MyTrait) { +//! // Request a &String from obj. +//! let _ = obj.get_context_by_ref::().unwrap(); +//! } +//! ``` +//! +//! In this example, if the concrete type of `obj` in `use_my_trait` is `SomeConcreteType`, then +//! the `get_context_ref` call will return a reference to `obj.some_string` with type `&String`. #![stable(feature = "rust1", since = "1.0.0")] @@ -700,3 +771,302 @@ pub const fn type_name() -> &'static str { pub const fn type_name_of_val(_val: &T) -> &'static str { type_name::() } + +/////////////////////////////////////////////////////////////////////////////// +// Provider trait +/////////////////////////////////////////////////////////////////////////////// + +/// Trait implemented by a type which can dynamically provide values based on type. +#[unstable(feature = "provide_any", issue = "96024")] +pub trait Provider { + /// Data providers should implement this method to provide *all* values they are able to + /// provide by using `demand`. + /// + /// Note that the `provide_*` methods on `Demand` have short-circuit semantics: if an earlier + /// method has successfully provided a value, then later methods will not get an opportunity to + /// provide. + /// + /// # Examples + /// + /// Provides a reference to a field with type `String` as a `&str`, and a value of + /// type `i32`. + /// + /// ```rust + /// # #![feature(provide_any)] + /// use std::any::{Provider, Demand}; + /// # struct SomeConcreteType { field: String, num_field: i32 } + /// + /// impl Provider for SomeConcreteType { + /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) { + /// demand.provide_ref::(&self.field) + /// .provide_value::(|| self.num_field); + /// } + /// } + /// ``` + #[unstable(feature = "provide_any", issue = "96024")] + fn provide<'a>(&'a self, demand: &mut Demand<'a>); +} + +/// Request a value from the `Provider`. +/// +/// # Examples +/// +/// Get a string value from a provider. +/// +/// ```rust +/// # #![feature(provide_any)] +/// use std::any::{Provider, request_value}; +/// +/// fn get_string(provider: &P) -> String { +/// request_value::(provider).unwrap() +/// } +/// ``` +#[unstable(feature = "provide_any", issue = "96024")] +pub fn request_value<'a, T, P>(provider: &'a P) -> Option +where + T: 'static, + P: Provider + ?Sized, +{ + request_by_type_tag::<'a, tags::Value, P>(provider) +} + +/// Request a reference from the `Provider`. +/// +/// # Examples +/// +/// Get a string reference from a provider. +/// +/// ```rust +/// # #![feature(provide_any)] +/// use std::any::{Provider, request_ref}; +/// +/// fn get_str(provider: &P) -> &str { +/// request_ref::(provider).unwrap() +/// } +/// ``` +#[unstable(feature = "provide_any", issue = "96024")] +pub fn request_ref<'a, T, P>(provider: &'a P) -> Option<&'a T> +where + T: 'static + ?Sized, + P: Provider + ?Sized, +{ + request_by_type_tag::<'a, tags::Ref>, P>(provider) +} + +/// Request a specific value by tag from the `Provider`. +fn request_by_type_tag<'a, I, P>(provider: &'a P) -> Option +where + I: tags::Type<'a>, + P: Provider + ?Sized, +{ + let mut tagged = TaggedOption::<'a, I>(None); + provider.provide(tagged.as_demand()); + tagged.0 +} + +/////////////////////////////////////////////////////////////////////////////// +// Demand and its methods +/////////////////////////////////////////////////////////////////////////////// + +/// A helper object for providing data by type. +/// +/// A data provider provides values by calling this type's provide methods. +#[unstable(feature = "provide_any", issue = "96024")] +#[repr(transparent)] +pub struct Demand<'a>(dyn Erased<'a> + 'a); + +impl<'a> Demand<'a> { + /// Create a new `&mut Demand` from a `&mut dyn Erased` trait object. + fn new<'b>(erased: &'b mut (dyn Erased<'a> + 'a)) -> &'b mut Demand<'a> { + // SAFETY: transmuting `&mut (dyn Erased<'a> + 'a)` to `&mut Demand<'a>` is safe since + // `Demand` is repr(transparent). + unsafe { &mut *(erased as *mut dyn Erased<'a> as *mut Demand<'a>) } + } + + /// Provide a value or other type with only static lifetimes. + /// + /// # Examples + /// + /// Provides a `String` by cloning. + /// + /// ```rust + /// # #![feature(provide_any)] + /// use std::any::{Provider, Demand}; + /// # struct SomeConcreteType { field: String } + /// + /// impl Provider for SomeConcreteType { + /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) { + /// demand.provide_value::(|| self.field.clone()); + /// } + /// } + /// ``` + #[unstable(feature = "provide_any", issue = "96024")] + pub fn provide_value(&mut self, fulfil: F) -> &mut Self + where + T: 'static, + F: FnOnce() -> T, + { + self.provide_with::, F>(fulfil) + } + + /// Provide a reference, note that the referee type must be bounded by `'static`, + /// but may be unsized. + /// + /// # Examples + /// + /// Provides a reference to a field as a `&str`. + /// + /// ```rust + /// # #![feature(provide_any)] + /// use std::any::{Provider, Demand}; + /// # struct SomeConcreteType { field: String } + /// + /// impl Provider for SomeConcreteType { + /// fn provide<'a>(&'a self, demand: &mut Demand<'a>) { + /// demand.provide_ref::(&self.field); + /// } + /// } + /// ``` + #[unstable(feature = "provide_any", issue = "96024")] + pub fn provide_ref(&mut self, value: &'a T) -> &mut Self { + self.provide::>>(value) + } + + /// Provide a value with the given `Type` tag. + fn provide(&mut self, value: I::Reified) -> &mut Self + where + I: tags::Type<'a>, + { + if let Some(res @ TaggedOption(None)) = self.0.downcast_mut::() { + res.0 = Some(value); + } + self + } + + /// Provide a value with the given `Type` tag, using a closure to prevent unnecessary work. + fn provide_with(&mut self, fulfil: F) -> &mut Self + where + I: tags::Type<'a>, + F: FnOnce() -> I::Reified, + { + if let Some(res @ TaggedOption(None)) = self.0.downcast_mut::() { + res.0 = Some(fulfil()); + } + self + } +} + +#[unstable(feature = "provide_any", issue = "96024")] +impl<'a> fmt::Debug for Demand<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Demand").finish_non_exhaustive() + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Type tags +/////////////////////////////////////////////////////////////////////////////// + +mod tags { + //! Type tags are used to identify a type using a separate value. This module includes type tags + //! for some very common types. + //! + //! Currently type tags are not exposed to the user. But in the future, if you want to use the + //! Provider API with more complex types (typically those including lifetime parameters), you + //! will need to write your own tags. + + use crate::marker::PhantomData; + + /// This trait is implemented by specific tag types in order to allow + /// describing a type which can be requested for a given lifetime `'a`. + /// + /// A few example implementations for type-driven tags can be found in this + /// module, although crates may also implement their own tags for more + /// complex types with internal lifetimes. + pub trait Type<'a>: Sized + 'static { + /// The type of values which may be tagged by this tag for the given + /// lifetime. + type Reified: 'a; + } + + /// Similar to the [`Type`] trait, but represents a type which may be unsized (i.e., has a + /// `?Sized` bound). E.g., `str`. + pub trait MaybeSizedType<'a>: Sized + 'static { + type Reified: 'a + ?Sized; + } + + impl<'a, T: Type<'a>> MaybeSizedType<'a> for T { + type Reified = T::Reified; + } + + /// Type-based tag for types bounded by `'static`, i.e., with no borrowed elements. + #[derive(Debug)] + pub struct Value(PhantomData); + + impl<'a, T: 'static> Type<'a> for Value { + type Reified = T; + } + + /// Type-based tag similar to [`Value`] but which may be unsized (i.e., has a `'Sized` bound). + #[derive(Debug)] + pub struct MaybeSizedValue(PhantomData); + + impl<'a, T: ?Sized + 'static> MaybeSizedType<'a> for MaybeSizedValue { + type Reified = T; + } + + /// Type-based tag for reference types (`&'a T`, where T is represented by + /// `>::Reified`. + #[derive(Debug)] + pub struct Ref(PhantomData); + + impl<'a, I: MaybeSizedType<'a>> Type<'a> for Ref { + type Reified = &'a I::Reified; + } +} + +/// An `Option` with a type tag `I`. +/// +/// Since this struct implements `Erased`, the type can be erased to make a dynamically typed +/// option. The type can be checked dynamically using `Erased::tag_id` and since this is statically +/// checked for the concrete type, there is some degree of type safety. +#[repr(transparent)] +struct TaggedOption<'a, I: tags::Type<'a>>(Option); + +impl<'a, I: tags::Type<'a>> TaggedOption<'a, I> { + fn as_demand(&mut self) -> &mut Demand<'a> { + Demand::new(self as &mut (dyn Erased<'a> + 'a)) + } +} + +/// Represents a type-erased but identifiable object. +/// +/// This trait is exclusively implemented by the `TaggedOption` type. +unsafe trait Erased<'a>: 'a { + /// The `TypeId` of the erased type. + fn tag_id(&self) -> TypeId; +} + +unsafe impl<'a, I: tags::Type<'a>> Erased<'a> for TaggedOption<'a, I> { + fn tag_id(&self) -> TypeId { + TypeId::of::() + } +} + +#[unstable(feature = "provide_any", issue = "96024")] +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_mut(&mut self) -> Option<&mut TaggedOption<'a, I>> + where + I: tags::Type<'a>, + { + if self.tag_id() == TypeId::of::() { + // SAFETY: Just checked whether we're pointing to an I. + Some(unsafe { &mut *(self as *mut Self).cast::>() }) + } else { + None + } + } +} diff --git a/library/core/tests/any.rs b/library/core/tests/any.rs index 0dffd137565b3..cdc6fadbab707 100644 --- a/library/core/tests/any.rs +++ b/library/core/tests/any.rs @@ -130,3 +130,65 @@ fn distinct_type_names() { assert_ne!(type_name_of_val(Velocity), type_name_of_val(Velocity(0.0, -9.8)),); } + +// Test the `Provider` API. + +struct SomeConcreteType { + some_string: String, +} + +impl Provider for SomeConcreteType { + fn provide<'a>(&'a self, demand: &mut Demand<'a>) { + demand + .provide_ref::(&self.some_string) + .provide_ref::(&self.some_string) + .provide_value::(|| "bye".to_owned()); + } +} + +// Test the provide and request mechanisms with a by-reference trait object. +#[test] +fn test_provider() { + let obj: &dyn Provider = &SomeConcreteType { some_string: "hello".to_owned() }; + + assert_eq!(&**request_ref::(obj).unwrap(), "hello"); + assert_eq!(&*request_value::(obj).unwrap(), "bye"); + assert_eq!(request_value::(obj), None); +} + +// Test the provide and request mechanisms with a boxed trait object. +#[test] +fn test_provider_boxed() { + let obj: Box = Box::new(SomeConcreteType { some_string: "hello".to_owned() }); + + assert_eq!(&**request_ref::(&*obj).unwrap(), "hello"); + assert_eq!(&*request_value::(&*obj).unwrap(), "bye"); + assert_eq!(request_value::(&*obj), None); +} + +// Test the provide and request mechanisms with a concrete object. +#[test] +fn test_provider_concrete() { + let obj = SomeConcreteType { some_string: "hello".to_owned() }; + + assert_eq!(&**request_ref::(&obj).unwrap(), "hello"); + assert_eq!(&*request_value::(&obj).unwrap(), "bye"); + assert_eq!(request_value::(&obj), None); +} + +trait OtherTrait: Provider {} + +impl OtherTrait for SomeConcreteType {} + +impl dyn OtherTrait { + fn get_ref(&self) -> Option<&T> { + request_ref::(self) + } +} + +// Test the provide and request mechanisms via an intermediate trait. +#[test] +fn test_provider_intermediate() { + let obj: &dyn OtherTrait = &SomeConcreteType { some_string: "hello".to_owned() }; + assert_eq!(obj.get_ref::().unwrap(), "hello"); +} diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 9505ec31609f5..d16c265d41a97 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -97,6 +97,7 @@ #![feature(const_slice_from_ref)] #![feature(waker_getters)] #![feature(slice_flatten)] +#![feature(provide_any)] #![deny(unsafe_op_in_unsafe_fn)] extern crate test;