diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 72528185707a6..4689b95b9776b 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,11 +85,76 @@ //! 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`. Note that end users +//! should not call `request_*` directly, they are helper functions for intermediate implementers +//! to use to implement a user-facing interface. +//! +//! Typically, a data provider is a trait object of a trait which extends `Provider`. A user will +//! request data from the trait object by specifying the type. +//! +//! ## Data flow +//! +//! * A user requests an object, which is delegated to `request_value` or `request_ref` +//! * `request_*` creates a `Demand` object and passes it to `Provider::provide` +//! * The object 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, it will be stored in the `Demand` object. +//! * `request_*` unpacks the `Demand` object and returns any stored value to the user. +//! +//! ## Examples +//! +//! ``` +//! # #![allow(incomplete_features)] +//! # #![feature(provide_any)] +//! # #![feature(trait_upcasting)] +//! use std::any::{Provider, Demand, request_ref}; +//! +//! // Definition of MyTrait +//! trait MyTrait: Provider { +//! // ... +//! } +//! +//! // Methods on `MyTrait` trait objects. +//! impl dyn MyTrait + '_ { +//! /// Common case: get a reference to a field of the error. +//! pub fn get_context_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, req: &mut Demand<'a>) { +//! req.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_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`. #![stable(feature = "rust1", since = "1.0.0")] use crate::fmt; use crate::intrinsics; +use crate::mem::transmute; /////////////////////////////////////////////////////////////////////////////// // Any trait @@ -700,3 +769,201 @@ 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 = "none")] +pub trait Provider { + /// Object providers should implement this method to provide *all* values they are able to + /// provide using `req`. + #[unstable(feature = "provide_any", issue = "none")] + fn provide<'a>(&'a self, req: &mut Demand<'a>); +} + +/// Request a value from the `Provider`. +#[unstable(feature = "provide_any", issue = "none")] +pub fn request_value<'a, T: 'static>(provider: &'a dyn Provider) -> Option { + request_by_type_tag::<'a, tags::Value>(provider) +} + +/// Request a reference from the `Provider`. +#[unstable(feature = "provide_any", issue = "none")] +pub fn request_ref<'a, T: ?Sized + 'static>(provider: &'a dyn Provider) -> Option<&'a T> { + request_by_type_tag::<'a, tags::Ref>>(provider) +} + +/// Request a specific value by tag from the `Provider`. +fn request_by_type_tag<'a, I>(provider: &'a dyn Provider) -> Option +where + I: tags::Type<'a>, +{ + let mut tagged = TaggedOption::<'a, I>(None); + provider.provide(tagged.as_demand()); + tagged.0 +} + +/////////////////////////////////////////////////////////////////////////////// +// Demand and its methods +/////////////////////////////////////////////////////////////////////////////// + +/// A helper object for providing objects by type. +/// +/// An object provider provides values by calling this type's provide methods. +#[allow(missing_debug_implementations)] +#[unstable(feature = "provide_any", issue = "none")] +// SAFETY: `TaggedOption::as_demand` relies on this precise definition. +#[repr(transparent)] +pub struct Demand<'a>(dyn Erased<'a> + 'a); + +impl<'a> Demand<'a> { + /// Provide a value or other type with only static lifetimes. + #[unstable(feature = "provide_any", issue = "none")] + 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. + #[unstable(feature = "provide_any", issue = "none")] + 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 + } +} + +/////////////////////////////////////////////////////////////////////////////// +// 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. + //! + //! Many users of the provider APIs will not need to use type tags at all. But if you want to + //! use them 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 element. + #[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 `&'a T` types. + #[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> { + // SAFETY: transmuting `&mut (dyn Erased<'a> + 'a)` to `&mut Demand<'a>` is safe since + // `Demand` is repr(transparent) and holds only a `dyn Erased<'a> + 'a`. + unsafe { transmute(self as &mut (dyn Erased<'a> + 'a)) } + } +} + +/// Represents a type-erased but identifiable object. +/// +/// This trait is exclusively implemented by the `TaggedOption` type. +trait Erased<'a>: 'a { + /// The `TypeId` of the erased type. + fn tag_id(&self) -> TypeId; +} + +impl<'a, I: tags::Type<'a>> Erased<'a> for TaggedOption<'a, I> { + fn tag_id(&self) -> TypeId { + TypeId::of::() + } +} + +#[unstable(feature = "provide_any", issue = "none")] +impl<'a> dyn Erased<'a> { + /// Returns some reference to the dynamic value if it is tagged with `I`, + /// or `None` if it isn't. + #[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 as *mut TaggedOption<'a, I>) }) + } else { + None + } + } +} diff --git a/library/core/tests/any.rs b/library/core/tests/any.rs index b36d6f0d40405..ff941635e2140 100644 --- a/library/core/tests/any.rs +++ b/library/core/tests/any.rs @@ -127,3 +127,25 @@ 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, req: &mut Demand<'a>) { + req.provide_ref::(&self.some_string) + .provide_value::(|| "bye".to_owned()); + } +} + +#[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); +} diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 841c114063dc1..85b53283a6159 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -88,6 +88,7 @@ #![feature(unzip_option)] #![feature(const_array_from_ref)] #![feature(const_slice_from_ref)] +#![feature(provide_any)] #![deny(unsafe_op_in_unsafe_fn)] extern crate test;