Add provide_any module to core by nrc · Pull Request #3192 · rust-lang/rfcs · Gi...
source link: https://github.com/rust-lang/rfcs/pull/3192
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Conversation
This RFC proposes adding a provide_any
module to the core library. The module provides a generic API for objects to provide type-based access to data. (In contrast to the any
module which provides type-driven downcasting, the proposed module integrates downcasting into data access to provide a safer and more ergonomic API).
Copy link
zkat commented 12 days ago
If it helps at all, this looks like it would be amaaaazing for miette
. I believe that with this in place, miette could stop defining its own Diagnostic
type and "simply" get users to provide various bits of context that it knows about. That means miette and its reporters would be able to use Eyre directly, instead of a bad fork of it :)
// Help text suggestion.
// Note, we should use a newtype here to prevent confusing different string context information.
if let Some(suggestion) = e.get_context_ref::<str>() {
I think it might be good to see this actually fleshed out properly here. It hints at a feature of this scheme that might benefit from some stronger up-front guidance.
In the accessor method approach you have a closed set of possible things to access along with what those things look like. That has its own upsides and downsides. Here, it’s more convention-driven, where instead of being offered a closed set of methods like backtrace
or suggestion
you agree on some of an open set of types, like Backtrace
or Suggestion
. In that convention-based approach any non-semantic types like str
seem meaningless to provide or ask for.
`provide_any` could live in its own crate, rather than in libcore. However, this would not be useful for `Error`.
`provide_any` could be a module inside `any` rather than a sibling (it could then be renamed to `provide` or `provider`).
Living under any
rather than in a new top-level module seems like a more natural home for this to me. There's overlap between type tags and type ids, and Any
and Provider
that I think we could give proper treatment together under the same umbrella.
that makes sense! I'm a bit hesitant because type ids and type tags are so obviously similar but have nothing in common in their interfaces or how they are used, so I thought it might be a bit confusing to have them together
Do you think trying to come up with a module-level doc for a hypothetical combined any
module might be a good exercise for identifying those cases where that confusion can come from?
The `TypeTag` trait is an abstraction over all type tags. It does not have any methods, only an associated type for the type which the tag represents. E.g., `<Ref<T> as TypeTag<'a>>::Type` is `&'a T`.
There is no universal type tag. A concrete type tag must be written for a 'category' of types. A few common tags are provided in `provide_any::tags`, including `Value` for any type bounded by `'static`, and `Ref` for types of the form `&'a T` where `T: 'static`. For less common types, the user must provide a tag which implements `TypeTag`; in this way the `provide_any` API generalises to all types.
Why is Ref
not a type tag combinator, where if T
has a type tag then Ref
can be used as type tag for references to T
(somewhat similar to the Option
combinator that does seem to exist)?
}
/// Tag combinator to wrap the given tag's value in an `Option<T>`
pub struct Optional<I>(PhantomData<I>);
Along with the Optional
combinator, can we have a Resultant
combinator?
It seems weird to support Option
-wrapping, but not Result
-wrapping. And it would also help illustrate the expressive power of the type tag design.
`provide_any` could be a module inside `any` rather than a sibling (it could then be renamed to `provide` or `provider`).
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.
Nitpick: quote crate names for readability and consistency
I found a nice usecase for something like this in my day-to-day codebase, which is a storage engine, that I thought I'd share. Individual documents can be cached for better performance. Caching is fine-grained, so for each document we can choose a different caching strategy. We've got a trait for these cache entries that looks like this:
pub trait CacheEntry: Any + Send + Sync + Display { /** The raw on-disk payload bytes. */ fn bytes_from_disk(&self) -> Option<BytesFromDisk> { None } /** The payload bytes that are yielded to callers. This may involve decompression or other transformations over the raw on-disk payload. */ fn bytes(&self) -> Option<Bytes> { None } /** The approximate size of this entry in-memory. */ fn approximate_size(&self) -> usize; }
The combination of Any
as a supertrait plus some optional getters are a pretty clear smell for Provider
. The reason this type is Any
at all is so different layers of the storage engine can cache differently shaped things, like a fully deserialized value for frequently accessed values. The Any
trait just gives us a single extensibility point that only supports a single specialized usecase at a time, with Provider
we'd simply provide another kind of thing a caller can ask for.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK