Skip to content

Commit 8aaedb4

Browse files
committed
Add ResultTag tag and other minor changes
Signed-off-by: Nick Cameron <[email protected]>
1 parent 3296f89 commit 8aaedb4

File tree

1 file changed

+22
-7
lines changed

1 file changed

+22
-7
lines changed

text/0000-dyno.md

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ pub mod error {
111111

112112
`get_context_ref` is added as a method on `Error` trait objects (`Error` is also likely to support similar methods for values and possibly other types, but I will elide those details), it simply calls `provide_any::request_by_type_tag` (we'll discuss this function and the `tags::Ref` passed to it below). But where does the context data come from? If a concrete error type supports backtraces, then it must override the `provide_context` method when implementing `Error` (by default, the method does nothing, i.e., no data is provided, so `get_context_ref` will always returns `None`). `provide_context` is used in the blanket implementation of `Provider` for `Error` types, in other words `Provider::provide` is delegated to `Error::provide_context`.
113113

114-
In `provide_context`, an error type provides access to data via a `Requisition` object, e.g., `req.provide_ref::<Backtrace>(&self.backtrace)`. The type of the reference passed to `provide_ref` is important here (and we encourage users to use explicit types with turbofish syntax even though it is not necessary). When a user calls `get_context_ref`, the requested type must match the type of an argument to `provide_ref`, e.g., the type of `&self.backtrace` is `&Backtrace`, so a call to `get_context_ref::<Backtrace>()` will return a reference to `self.backtrace`. An implementer can make multiple calls to `provide_ref` to provide multiple data with different types.
114+
In `provide_context`, an error type provides access to data via a `Requisition` object, e.g., `req.provide_ref::<Backtrace>(&self.backtrace)`. The type of the reference passed to `provide_ref` is important here (and we encourage users to use explicit types with turbofish syntax even though it is not necessary, this might even be possible to enforce using a lint). When a user calls `get_context_ref`, the requested type must match the type of an argument to `provide_ref`, e.g., the type of `&self.backtrace` is `&Backtrace`, so a call to `get_context_ref::<Backtrace>()` will return a reference to `self.backtrace`. An implementer can make multiple calls to `provide_ref` to provide multiple data with different types.
115115

116116
Note that `Requisition` has methods for providing values as well as references, and for providing more complex types. These will be covered in the next section.
117117

@@ -193,11 +193,18 @@ pub mod provide_any {
193193
impl<'a, I: TypeTag<'a>> TypeTag<'a> for Optional<I> {
194194
type Type = Option<I::Type>;
195195
}
196+
197+
/// Tag combinator to wrap the given tag's value in an `Result<T, E>`
198+
pub struct ResultTag<I, E>(PhantomData<I>, PhantomData<E>);
199+
200+
impl<'a, I: TypeTag<'a>, E: TypeTag<'a>> TypeTag<'a> for ResultTag<I, E> {
201+
type Type = Result<I::Type, E::Type>;
202+
}
196203
}
197204
}
198205
```
199206

200-
For intermediate libraries, `Value` serves the common case of providing a new or temporary value (for example, computing a `String` message from multiple fields), `Ref` and `RefMut` cover the common case of providing a reference to a field of the providing object (as in `Error::context_ref` providing a reference to `self.backtrace` in the motivating examples), where the field does not contain non-static references. `Optional` is used in the implementation of `provide_any`, it is public since it seems like it could be generally useful.
207+
For intermediate libraries, `Value` serves the common case of providing a new or temporary value (for example, computing a `String` message from multiple fields), `Ref` and `RefMut` cover the common case of providing a reference to a field of the providing object (as in `Error::context_ref` providing a reference to `self.backtrace` in the motivating examples), where the field does not contain non-static references. `Optional` is used in the implementation of `provide_any`, it is public since it seems like it could be generally useful. `ResultTag` is not strictly necessary, but is included as a complement to `Optional`.
201208

202209
### Requisition
203210

@@ -231,12 +238,20 @@ impl<'a, 'b> Requisition<'a, 'b> {
231238

232239
`provide_value` and `provide_ref` are convenience methods for the common cases of providing a temporary value and providing a reference to a field of `self`, respectively. `provide_value` takes a function to avoid unnecessary work when querying for data of a different type; `provide_ref` does not use a function since creating a reference is typically cheap.
233240

234-
`provide` and `provide_with` offer full generality, but require the explicit use of type tags.
241+
`provide` and `provide_with` offer full generality, but require the explicit use of type tags. An example of using `provide_with`:
235242

236-
It seems reasonable that data might be accessed and provided on different threads. For this purpose, `provide_any` includes a version of `Requisition` which implements `Send`: `SendRequisition`. An open question is if it is also useful to support `Sync` variations (and if there is a better name).
243+
```rust
244+
impl Error for MyError {
245+
fn provide_context<'a>(&'a self, mut req: Requisition<'a, '_>) {
246+
// Taking ownership of the string implies cloning it, so we use `provide_with` to avoid that
247+
// operation unless it is necessary.
248+
req.provide_with::<String>(|| self.suggestion.to_owned());
249+
}
250+
}
251+
```
237252

253+
It seems reasonable that data might be accessed and provided on different threads. For this purpose, `provide_any` includes a version of `Requisition` which implements `Send`: `SendRequisition`. An open question is if it is also useful to support `Sync` variations (and if there is a better name).
238254

239-
TODO docs from crate
240255

241256
# Drawbacks
242257
[drawbacks]: #drawbacks
@@ -257,9 +272,9 @@ Each of these approaches has significant downsides: adding methods to traits lea
257272

258273
`provide_any` could live in its own crate, rather than in libcore. However, this would not be useful for `Error`.
259274

260-
`provide_any` could be a module inside `any` rather than a sibling (it could then be renamed to `provide` or `provider`).
275+
`provide_any` could be a module inside `any` rather than a sibling (it could then be renamed to `provide` or `provider`), or the contents of `provide_any` could be added to `any`.
261276

262-
There are numerous ways to tweak the API of a module like `provide_any`. The dyno and object-provider crates provide two such examples. There are many others, for example providing more patterns of types without requiring tags, not providing any common type patterns (i.e., always requiring tags), not exposing tags at all, etc.
277+
There are numerous ways to tweak the API of a module like `provide_any`. The `dyno` and `object-provider` crates provide two such examples. There are many others, for example providing more patterns of types without requiring tags, not providing any common type patterns (i.e., always requiring tags), not exposing tags at all, etc.
263278

264279
One hazard that must be avoided with such tweaks is that the API must distinguish between reference types with a `'static` lifetime and value types (with no lifetimes), either by using type tags or by having separate mechanisms for handling references and values. If this is done improperly, the API could be unsound. As an example, consider an API which lets an object provide a reference with type `&'a str` (where `'a` is the lifetime of some local scope), then a caller requests an object of type `&'static str` using an API designed for values (possible because that type satisfies the `'static` bound). Without some care, it is possible for the two types to be confused (because the lifetime is not reflected in the type tag or `TypeId`) and for the `&'a str` to be returned with the wrong lifetime. I believe this is not possible in the current proposal, but was possible in an earlier, more ergonomic iteration.
265280

0 commit comments

Comments
 (0)