A question about Provider API

I've closely followed the RFC and the discussion around Provider API and I have found little information about the drawbacks of the approach (apart from the possible overlap with Any): I failed to find any rational of why the functionality is needed in the first place and what are the design drawbacks of adding it.

Coming from .Net ecosystem I perceive generic access to errors' inner data as a hacky way to emulate typed exception handling, which, at least as far as I can tell from my .Net experience, is often utilized to quickly handle non-erroneous scenarios by error handling facilities, namely, route the results of not-actually-an-error calls somewhere. Even the typed exception handling makes code more difficult to reason about given the more complex control flow, but "Provider API" gives the ability to pass around arbitrary data completely hidden (on the type level) and, with interior mutability, potentially allowing any logic to be called from anywhere in the callstack.

Are there any discussions/articles/documents on how the "Provider API" is assumed to be utilized and how it potentially affects the complexity?

1 Like

I believe the primary motivation is to simultaneously have

without also demanding that the Backtrace type itself is in core.

More broadly, the same type of situation can arise when:

  • crate_one and crate_two depend on some_common_crate, and
  • you have information of type crate_one::Something which needs to be fetched by crate_two,
  • through a shared non-generic mechanism provided by some_common_crate.

Ideally, the common mechanism would be generic, so that crate_one can declare its provision in a statically verifiable fashion, but in practice that is often infeasible — and I would say, not because of deficiencies in Rust, but because every static type system eventually runs into expressiveness limits. In the case of Error, the root problem is that Error::source() returns a &dyn Error, so it needs to be possible to get the additional information out of a dyn Error. The provider mechanism allows extension of something otherwise inextensible.

1 Like

To give a more specific case of the the broader situation, there's a type SpanTrace which is basically a backtrace but made of tracing-spans instead of function frames, there are crates like color-eyre that integrate support for capturing and printing these spantraces along with backtraces. Even if std::error::Error were to integrate backtrace support so that error chains can check if any of the causes provide a backtrace, it won't integrate spantrace support so there's no way to inspect the spantrace from the innermost error where it would have the most context.

1 Like

Note that sometimes what the callee considers an error, the caller can actually recover from and would like to inspect and handle separately. Being able to "split hairs" like this is useful (e.g., a "cannot find command" is probably not fixable, but "5xx status code" is possibly worth a retry with backoff).

Thank you. I understand. But I'm more interested to learn about the possible risks of the current solution.

As I see it, implementing multiple v-tables and allowing passing Box<dyn Error + MyContext + ImportantContext + HasSpanTrace + HasDumpAttached> is much more clean, explicit and easier to reason about. What frightens me is the ability to pass hidden possibly mutable state around via error chains.

There's nowhere to attach those extra traits to, you have to fit everything through a dyn Error shaped hole to support Error::source.

2 Likes

Thank you.

Thank you, @kpreid, @Nemo157.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.