pub struct Request<'a>(/* private fields */);
error_generic_member_access
Request supports generic, type-driven access to data. Its use is currently restricted to the standard library in cases where trait authors wish to allow trait implementors to share generic information across trait boundaries. The motivating and prototypical use case is core::error::Error which would otherwise require a method per concrete type (eg. std::backtrace::Backtrace instance that implementors want to expose to users).
Request
core::error::Error
std::backtrace::Backtrace
To describe the intended data flow for Request objects, let’s consider two conceptual users separated by API boundaries:
Consumer - the consumer requests objects using a Request instance; eg a crate that offers fancy Error/Result reporting to users wants to request a Backtrace from a given dyn Error.
Error
Result
dyn Error
Producer - the producer provides objects when requested via Request; eg. a library with an an Error implementation that automatically captures backtraces at the time instances are created.
The consumer only needs to know where to submit their request and are expected to handle the request not being fulfilled by the use of Option<T> in the responses offered by the producer.
Option<T>
backtrace::Backtrace
request_ref
request_value
None
Option
The best way to demonstrate this is using an example implementation of Error’s provide trait method:
provide
#![feature(error_generic_member_access)] use core::fmt; use core::error::Request; use core::error::request_ref; #[derive(Debug)] enum MyLittleTeaPot { Empty, } #[derive(Debug)] struct MyBacktrace { / ... } impl MyBacktrace { fn new() -> MyBacktrace { / ... } } #[derive(Debug)] struct Error { backtrace: MyBacktrace, } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Example Error") } } impl std::error::Error for Error { fn provide<'a>(&'a self, request: &mut Request<'a>) { request .provide_ref::<MyBacktrace>(&self.backtrace); } } fn main() { let backtrace = MyBacktrace::new(); let error = Error { backtrace }; let dyn_error = &error as &dyn std::error::Error; let backtrace_ref = request_ref::<MyBacktrace>(dyn_error).unwrap(); assert!(core::ptr::eq(&error.backtrace, backtrace_ref)); assert!(request_ref::<MyLittleTeaPot>(dyn_error).is_none()); }
Provides a value or other type with only static lifetimes.
Provides an u8.
u8
#![feature(error_generic_member_access)] use core::error::Request; #[derive(Debug)] struct SomeConcreteType { field: u8 } impl std::fmt::Display for SomeConcreteType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{} failed", self.field) } } impl std::error::Error for SomeConcreteType { fn provide<'a>(&'a self, request: &mut Request<'a>) { request.provide_value::<u8>(self.field); } }
Provides a value or other type with only static lifetimes computed using a closure.
Provides a String by cloning.
String
#![feature(error_generic_member_access)] use core::error::Request; #[derive(Debug)] struct SomeConcreteType { field: String } impl std::fmt::Display for SomeConcreteType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{} failed", self.field) } } impl std::error::Error for SomeConcreteType { fn provide<'a>(&'a self, request: &mut Request<'a>) { request.provide_value_with::<String>(|| self.field.clone()); } }
Provides a reference. The referee type must be bounded by 'static, but may be unsized.
'static
Provides a reference to a field as a &str.
&str
#![feature(error_generic_member_access)] use core::error::Request; #[derive(Debug)] struct SomeConcreteType { field: String } impl std::fmt::Display for SomeConcreteType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{} failed", self.field) } } impl std::error::Error for SomeConcreteType { fn provide<'a>(&'a self, request: &mut Request<'a>) { request.provide_ref::<str>(&self.field); } }
Provides a reference computed using a closure. The referee type must be bounded by 'static, but may be unsized.
#![feature(error_generic_member_access)] use core::error::Request; #[derive(Debug)] struct SomeConcreteType { business: String, party: String } fn today_is_a_weekday() -> bool { true } impl std::fmt::Display for SomeConcreteType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{} failed", self.business) } } impl std::error::Error for SomeConcreteType { fn provide<'a>(&'a self, request: &mut Request<'a>) { request.provide_ref_with::<str>(|| { if today_is_a_weekday() { &self.business } else { &self.party } }); } }
Checks if the Request 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.
Checks if a u8 still needs to be provided and then provides it.
#![feature(error_generic_member_access)] use core::error::Request; use core::error::request_value; #[derive(Debug)] struct Parent(Option<u8>); impl std::fmt::Display for Parent { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "a parent failed") } } impl std::error::Error for Parent { fn provide<'a>(&'a self, request: &mut Request<'a>) { if let Some(v) = self.0 { request.provide_value::<u8>(v); } } } #[derive(Debug)] 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 std::fmt::Display for Child { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "child failed: \n because of parent: {}", self.parent) } } impl std::error::Error for Child { fn provide<'a>(&'a self, request: &mut Request<'a>) { / In general, we don't know if this call will provide / an `u8` value or not... self.parent.provide(request); / ...so we check to see if the `u8` is needed before / we run our expensive computation. if request.would_be_satisfied_by_value_of::<u8>() { if let Some(v) = self.an_expensive_computation() { request.provide_value::<u8>(v); } } / The request will be satisfied now, regardless of if / the parent provided the value or we did. assert!(!request.would_be_satisfied_by_value_of::<u8>()); } } let parent = Parent(Some(42)); let child = Child { parent }; assert_eq!(Some(42), request_value::<u8>(&child)); let parent = Parent(None); let child = Child { parent }; assert_eq!(Some(99), request_value::<u8>(&child));
Checks if the Request 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.
Checks if a &str still needs to be provided and then provides it.
#![feature(error_generic_member_access)] use core::error::Request; use core::error::request_ref; #[derive(Debug)] struct Parent(Option<String>); impl std::fmt::Display for Parent { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "a parent failed") } } impl std::error::Error for Parent { fn provide<'a>(&'a self, request: &mut Request<'a>) { if let Some(v) = &self.0 { request.provide_ref::<str>(v); } } } #[derive(Debug)] 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 std::fmt::Display for Child { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{} failed: \n {}", self.name, self.parent) } } impl std::error::Error for Child { fn provide<'a>(&'a self, request: &mut Request<'a>) { / In general, we don't know if this call will provide / a `str` reference or not... self.parent.provide(request); / ...so we check to see if the `&str` is needed before / we run our expensive computation. if request.would_be_satisfied_by_ref_of::<str>() { if let Some(v) = self.an_expensive_computation() { request.provide_ref::<str>(v); } } / The request will be satisfied now, regardless of if / the parent provided the reference or we did. assert!(!request.would_be_satisfied_by_ref_of::<str>()); } } let parent = Parent(Some("parent".into())); let child = Child { parent, name: "child".into() }; assert_eq!(Some("parent"), request_ref::<str>(&child)); let parent = Parent(None); let child = Child { parent, name: "child".into() }; assert_eq!(Some("child"), request_ref::<str>(&child));
TypeId
self