From bb5db85f745614a1929b6a60125953170cab7462 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Wed, 15 Dec 2021 16:17:16 +0000 Subject: [PATCH 1/7] Add the Provider api to core::any Signed-off-by: Nick Cameron --- library/core/src/any.rs | 273 +++++++++++++++++++++++++++++++++++++- library/core/tests/any.rs | 22 +++ library/core/tests/lib.rs | 1 + 3 files changed, 293 insertions(+), 3 deletions(-) diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 3b15ab1e6895b..1ca4e813443eb 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 0dffd137565b3..bf38207065faf 100644 --- a/library/core/tests/any.rs +++ b/library/core/tests/any.rs @@ -130,3 +130,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 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; From 57d027d23a12a956520063625072440ca74b171d Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Thu, 7 Apr 2022 15:34:36 +0100 Subject: [PATCH 2/7] Modify the signature of the request_* methods so that trait_upcasting is not required Signed-off-by: Nick Cameron --- library/core/src/any.rs | 25 ++++++++++++++++--------- library/core/tests/any.rs | 6 +++--- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 1ca4e813443eb..295ca00d0b4a1 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -110,9 +110,7 @@ //! ## Examples //! //! ``` -//! # #![allow(incomplete_features)] //! # #![feature(provide_any)] -//! # #![feature(trait_upcasting)] //! use std::any::{Provider, Demand, request_ref}; //! //! // Definition of MyTrait @@ -122,9 +120,9 @@ //! //! // Methods on `MyTrait` trait objects. //! impl dyn MyTrait + '_ { -//! /// Common case: get a reference to a field of the error. +//! /// Common case: get a reference to a field of the struct. //! pub fn get_context_ref(&self) -> Option<&T> { -//! request_ref::(self) +//! request_ref::(self) //! } //! } //! @@ -785,20 +783,29 @@ pub trait Provider { /// 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) +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`. #[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) +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>(provider: &'a dyn Provider) -> Option +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()); diff --git a/library/core/tests/any.rs b/library/core/tests/any.rs index bf38207065faf..14a2b09c95eb7 100644 --- a/library/core/tests/any.rs +++ b/library/core/tests/any.rs @@ -148,7 +148,7 @@ impl Provider for SomeConcreteType { 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); + assert_eq!(&**request_ref::(obj).unwrap(), "hello"); + assert_eq!(&*request_value::(obj).unwrap(), "bye"); + assert_eq!(request_value::(obj), None); } From 17730e66f6e1e8c6d5be490b27ee6cf6f30c0fda Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Tue, 12 Apr 2022 10:49:53 +0100 Subject: [PATCH 3/7] Update docs Signed-off-by: Nick Cameron --- library/core/src/any.rs | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 295ca00d0b4a1..ce8677d043957 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -91,20 +91,21 @@ //! `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 +//! 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. //! //! 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. +//! request data from a trait object by specifying the type of the data. //! //! ## Data flow //! -//! * A user requests an object, which is delegated to `request_value` or `request_ref` +//! * 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 object provider's implementation of `Provider::provide` tries providing values of +//! * 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, it will be stored in the `Demand` object. +//! 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 @@ -113,15 +114,15 @@ //! # #![feature(provide_any)] //! use std::any::{Provider, Demand, request_ref}; //! -//! // Definition of MyTrait +//! // Definition of MyTrait, a data provider. //! trait MyTrait: Provider { //! // ... //! } //! //! // Methods on `MyTrait` trait objects. //! impl dyn MyTrait + '_ { -//! /// Common case: get a reference to a field of the struct. -//! pub fn get_context_ref(&self) -> Option<&T> { +//! /// Get a reference to a field of the implementing struct. +//! pub fn get_context_by_ref(&self) -> Option<&T> { //! request_ref::(self) //! } //! } @@ -134,6 +135,8 @@ //! //! impl Provider for SomeConcreteType { //! fn provide<'a>(&'a self, req: &mut Demand<'a>) { +//! // Provide a string reference. We could provide multiple values with +//! // different types here. //! req.provide_ref::(&self.some_string); //! } //! } @@ -141,12 +144,12 @@ //! // Downstream usage of `MyTrait`. //! fn use_my_trait(obj: &dyn MyTrait) { //! // Request a &String from obj. -//! let _ = obj.get_context_ref::().unwrap(); +//! 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`. +//! the `get_context_ref` call will return a reference to `obj.some_string` with type `&String`. #![stable(feature = "rust1", since = "1.0.0")] @@ -775,10 +778,10 @@ pub const fn type_name_of_val(_val: &T) -> &'static str { /// 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`. + /// Data providers should implement this method to provide *all* values they are able to + /// provide by using `demand`. #[unstable(feature = "provide_any", issue = "none")] - fn provide<'a>(&'a self, req: &mut Demand<'a>); + fn provide<'a>(&'a self, demand: &mut Demand<'a>); } /// Request a value from the `Provider`. @@ -816,12 +819,11 @@ where // Demand and its methods /////////////////////////////////////////////////////////////////////////////// -/// A helper object for providing objects by type. +/// A helper object for providing data by type. /// -/// An object provider provides values by calling this type's provide methods. +/// A data 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); @@ -836,7 +838,8 @@ impl<'a> Demand<'a> { self.provide_with::, F>(fulfil) } - /// Provide a reference, note that the referee type must be bounded by `'static`, but may be unsized. + /// 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) @@ -902,7 +905,7 @@ mod tags { type Reified = T::Reified; } - /// Type-based tag for types bounded by `'static`, i.e., with no borrowed element. + /// Type-based tag for types bounded by `'static`, i.e., with no borrowed elements. #[derive(Debug)] pub struct Value(PhantomData); @@ -960,7 +963,7 @@ impl<'a, I: tags::Type<'a>> Erased<'a> for TaggedOption<'a, I> { #[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. + /// or `None` otherwise. #[inline] fn downcast_mut(&mut self) -> Option<&mut TaggedOption<'a, I>> where From e82368d6fc3336a1395c56a9bdc83fd76bbf4732 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Tue, 12 Apr 2022 21:38:27 +0100 Subject: [PATCH 4/7] Add examples to docs Signed-off-by: Nick Cameron --- library/core/src/any.rs | 78 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/library/core/src/any.rs b/library/core/src/any.rs index ce8677d043957..a4d902150f879 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -134,10 +134,10 @@ //! } //! //! impl Provider for SomeConcreteType { -//! fn provide<'a>(&'a self, req: &mut Demand<'a>) { +//! fn provide<'a>(&'a self, demand: &mut Demand<'a>) { //! // Provide a string reference. We could provide multiple values with //! // different types here. -//! req.provide_ref::(&self.some_string); +//! demand.provide_ref::(&self.some_string); //! } //! } //! @@ -780,11 +780,40 @@ pub const fn type_name_of_val(_val: &T) -> &'static str { pub trait Provider { /// Data providers should implement this method to provide *all* values they are able to /// provide by using `demand`. + /// + /// # Examples + /// + /// Provides a reference to a field with type `String` 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 = "none")] 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 = "none")] pub fn request_value<'a, T, P>(provider: &'a P) -> Option where @@ -795,6 +824,19 @@ where } /// 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 = "none")] pub fn request_ref<'a, T, P>(provider: &'a P) -> Option<&'a T> where @@ -829,6 +871,22 @@ pub struct Demand<'a>(dyn Erased<'a> + 'a); impl<'a> 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 = "none")] pub fn provide_value(&mut self, fulfil: F) -> &mut Self where @@ -840,6 +898,22 @@ impl<'a> Demand<'a> { /// 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 = "none")] pub fn provide_ref(&mut self, value: &'a T) -> &mut Self { self.provide::>>(value) From 2e0ca2537f0f8549e5b24ff7d0b849b61aba7414 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Thu, 14 Apr 2022 07:56:09 +0100 Subject: [PATCH 5/7] Add tracking issue number Signed-off-by: Nick Cameron --- library/core/src/any.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/library/core/src/any.rs b/library/core/src/any.rs index a4d902150f879..c0a76389b6819 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -776,7 +776,7 @@ pub const fn type_name_of_val(_val: &T) -> &'static str { /////////////////////////////////////////////////////////////////////////////// /// Trait implemented by a type which can dynamically provide values based on type. -#[unstable(feature = "provide_any", issue = "none")] +#[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`. @@ -796,7 +796,7 @@ pub trait Provider { /// } /// } /// ``` - #[unstable(feature = "provide_any", issue = "none")] + #[unstable(feature = "provide_any", issue = "96024")] fn provide<'a>(&'a self, demand: &mut Demand<'a>); } @@ -814,7 +814,7 @@ pub trait Provider { /// request_value::(provider).unwrap() /// } /// ``` -#[unstable(feature = "provide_any", issue = "none")] +#[unstable(feature = "provide_any", issue = "96024")] pub fn request_value<'a, T, P>(provider: &'a P) -> Option where T: 'static, @@ -837,7 +837,7 @@ where /// request_ref::(provider).unwrap() /// } /// ``` -#[unstable(feature = "provide_any", issue = "none")] +#[unstable(feature = "provide_any", issue = "96024")] pub fn request_ref<'a, T, P>(provider: &'a P) -> Option<&'a T> where T: 'static + ?Sized, @@ -865,7 +865,7 @@ where /// /// A data provider provides values by calling this type's provide methods. #[allow(missing_debug_implementations)] -#[unstable(feature = "provide_any", issue = "none")] +#[unstable(feature = "provide_any", issue = "96024")] #[repr(transparent)] pub struct Demand<'a>(dyn Erased<'a> + 'a); @@ -887,7 +887,7 @@ impl<'a> Demand<'a> { /// } /// } /// ``` - #[unstable(feature = "provide_any", issue = "none")] + #[unstable(feature = "provide_any", issue = "96024")] pub fn provide_value(&mut self, fulfil: F) -> &mut Self where T: 'static, @@ -914,7 +914,7 @@ impl<'a> Demand<'a> { /// } /// } /// ``` - #[unstable(feature = "provide_any", issue = "none")] + #[unstable(feature = "provide_any", issue = "96024")] pub fn provide_ref(&mut self, value: &'a T) -> &mut Self { self.provide::>>(value) } @@ -1034,7 +1034,7 @@ impl<'a, I: tags::Type<'a>> Erased<'a> for TaggedOption<'a, I> { } } -#[unstable(feature = "provide_any", issue = "none")] +#[unstable(feature = "provide_any", issue = "96024")] impl<'a> dyn Erased<'a> { /// Returns some reference to the dynamic value if it is tagged with `I`, /// or `None` otherwise. From 843f90cbb72758a091db25653ac515098f18399d Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Mon, 18 Apr 2022 12:37:54 +0100 Subject: [PATCH 6/7] Add some more tests Signed-off-by: Nick Cameron --- library/core/tests/any.rs | 44 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/library/core/tests/any.rs b/library/core/tests/any.rs index 14a2b09c95eb7..cdc6fadbab707 100644 --- a/library/core/tests/any.rs +++ b/library/core/tests/any.rs @@ -138,12 +138,15 @@ struct SomeConcreteType { } impl Provider for SomeConcreteType { - fn provide<'a>(&'a self, req: &mut Demand<'a>) { - req.provide_ref::(&self.some_string) + 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() }; @@ -152,3 +155,40 @@ fn test_provider() { 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"); +} From 66290109bbd0f2da2f6b2dc9787b29371e788c76 Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Wed, 11 May 2022 10:12:22 +0100 Subject: [PATCH 7/7] Address reviewer comments Signed-off-by: Nick Cameron --- library/core/src/any.rs | 55 +++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/library/core/src/any.rs b/library/core/src/any.rs index c0a76389b6819..5eda860264c6c 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -93,7 +93,9 @@ //! 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. +//! 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. @@ -155,7 +157,6 @@ use crate::fmt; use crate::intrinsics; -use crate::mem::transmute; /////////////////////////////////////////////////////////////////////////////// // Any trait @@ -781,18 +782,24 @@ 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`. + /// 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 } + /// # 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); + /// demand.provide_ref::(&self.field) + /// .provide_value::(|| self.num_field); /// } /// } /// ``` @@ -864,12 +871,18 @@ where /// A helper object for providing data by type. /// /// A data provider provides values by calling this type's provide methods. -#[allow(missing_debug_implementations)] #[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 @@ -943,6 +956,13 @@ impl<'a> Demand<'a> { } } +#[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 /////////////////////////////////////////////////////////////////////////////// @@ -951,9 +971,9 @@ 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. + //! 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; @@ -970,7 +990,7 @@ mod tags { } /// Similar to the [`Type`] trait, but represents a type which may be unsized (i.e., has a - /// `'Sized` bound). E.g., `str`. + /// `?Sized` bound). E.g., `str`. pub trait MaybeSizedType<'a>: Sized + 'static { type Reified: 'a + ?Sized; } @@ -995,7 +1015,8 @@ mod tags { type Reified = T; } - /// Type-based tag for `&'a T` types. + /// Type-based tag for reference types (`&'a T`, where T is represented by + /// `>::Reified`. #[derive(Debug)] pub struct Ref(PhantomData); @@ -1014,28 +1035,26 @@ 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)) } + 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. -trait Erased<'a>: 'a { +unsafe 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> { +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> { +impl<'a> dyn Erased<'a> + 'a { /// Returns some reference to the dynamic value if it is tagged with `I`, /// or `None` otherwise. #[inline] @@ -1045,7 +1064,7 @@ impl<'a> dyn Erased<'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>) }) + Some(unsafe { &mut *(self as *mut Self).cast::>() }) } else { None }