rust-lang / rust

Empowering everyone to build reliable and efficient software.

Home Page:https://www.rust-lang.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Tracking Issue for `Error` in `core`

lukas-code opened this issue · comments

commented

(This was merged a while ago without a tracking issue, so I'm creating this one here now.)

Feature gate: #![feature(error_in_core)]

This is a tracking issue for moving the Error trait to the core library.

Public API

// core::error
pub trait Error: Debug + Display  {
    /* same API as std::error::Error */
}

// unstable in core, stable in std
impl dyn Error + 'static [+ Send [+ Sync]] {
    pub fn is<T: Error + 'static>(&self) -> bool;
    pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T>;
    pub fn downcast_mut<T: Error + 'static>(&mut self) -> Option<&mut T>;
}

// unstable in alloc, stable in std
impl dyn Error [+ Send [+ Sync]] {
    pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<Self>>;
}

Steps / History

  • Implementation (first attempt, closed): #90328
  • Implementation (merged): #99917
  • Stabilize the Provider API: #96024
  • Stabilize error_generic_member_access: #99301
  • Final comment period (FCP)1
  • Stabilization PR

Unresolved Questions

  • None yet.

Footnotes

  1. https://std-dev-guide.rust-lang.org/feature-lifecycle/stabilization.html

commented

@rustbot label B-unstable A-error-handling

Error: Label B-unstable can only be set by Rust team members

Please file an issue on GitHub at triagebot if there's a problem with this bot, or reach out on #t-infra on Zulip.

I'm excited about this feature, so if there's anything I can do to help pushing it over the finish line I'd be happy to help. I'm fairly experienced with programming in Rust and I've been silently following various Rust features from RFC to stabilization, but I haven't myself contributed to the compiler or standard library yet.

I think this feature could turn out to be a considerable improvement to Rust. There are quite a few small-ish crates out there that only perform well-specified pure-function-like operations. I can imagine that many of these crates could trivially be no_std compatible by default (without even requiring a cargo feature gate) were it not for their error types implementing std::error::Error.

@robamler that would be lovely, I'm happy to hand off and help coordinate that work. I've been focused on governance work since august and haven't had much time for pushing this across the finish line.

I believe the current blockers for this are resolving issues in the provider / generic member access APIs

This comment by @dtolnay best outlines the issues that still need to be resolved: #99301 (comment)

Let me know if you have any further questions. The zulip is probably the best way to reach me quickly. I'm not keeping on top of github notifications right now since I still need to unsubscribe to a bunch of things to turn down the volume of notifications I get daily.

@yaahc Thank you for the references!

Do #![feature(error_in_core)] and #![feature(error_generic_member_access)] really have to be stabilized together? Maybe I'm missing something here, but it looks like the most pragmatic way forward would be to

  1. first stabilize error_in_core with your implementation in #99917, except that the method core::error::Error::provide would still be unstable with #![feature(error_generic_member_access)], just as std::error::Error::provide is today; and
  2. then resolve any issues that are blocking #![feature(error_generic_member_access)] from stabilization; once those are resolved, both std::error::Error::provide and core::error::Error::provide would become available on stable.

(I'll reach out on zulip if I don't get any response here; just wanted to leave this comment here as a kind of documentation in case I can't figure this out and someone else needs to take over.)

I think in theory we could stabilize error-in-core before provider but I'm worried since we removed fn backtrace on the basis of the provider API, if we lock error into core we can never add fn backtrace back, and if we end up with some unavoidable blocker to stabilizing provider we may end up in a worse situation than we started.

I see, thanks! So I understand that maybe #99301 should be resolved first. I'm following up with the discussion there.

Hey folks, is it planned to implement Sized?
It would nice to have it work...

... with Result:

the size for values of type `(dyn core::error::Error + 'static)` cannot be known at compilation time [E0277] doesn't have a size known at compile-time Help: the trait `core::marker::Sized` is not implemented for `(dyn core::error::Error + 'static)` Note: required by a bound in `core::result::Result`

... with From:

the size for values of type `(dyn core::error::Error + 'static)` cannot be known at compilation time [E0277] doesn't have a size known at compile-time Help: within `error::Error`, the trait `core::marker::Sized` is not implemented for `(dyn core::error::Error + 'static)` Note: required because it appears within the type `error::Error` Note: required by a bound in `core::convert::From`

Hey folks, is it planned to implement Sized?

I'm not positive I'm interpreting "implement Sized" correctly.

Assuming you mean add Sized as a supertrait of the Error trait, the answer is a definite no. Doing so would break the ability to create trait objects and would be backwards incompatible as well as defeating the purpose of the error trait.

error[E0038]: the trait `Error` cannot be made into an object
 --> src\main.rs:3:37
  |
3 | static_assertions::assert_impl_all!(dyn Error: Error);
  |                                     ^^^^^^^^^ `Error` cannot be made into an object
  |
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
 --> src\main.rs:1:14
  |
1 | trait Error: Sized {}
  |       -----  ^^^^^ ...because it requires `Self: Sized`
  |       |
  |       this trait cannot be made into an object...

And here's the link from the above diagnostic for more info: https://doc.rust-lang.org/reference/items/traits.html#object-safety

If I've misinterpreted your question please let me know.

Is there a FCP on the horizon for this feature? It seems relatively straight-forward insofar as its featureset, and it would be nice to allow thing like anyhow to take advantage of core::error::Error on stable.

https://doc.rust-lang.org/stable/core/error/trait.Error.html does not show that this enum is unstable which can be misleading to the end user (it was to me! Yes, I know core::error is what's displayed as unstable)

From reading this tracking issue and the two blocking tracking issues, it seems like feature(error_generic_member_access) continues to be an area of active development and iteration. Is there any way stabilization of core::error::Error could be decoupled from stabilization of the reflection-powered backtrace access?

There's a comment that removal of Error::backtrace() was predicated on existence of Provider (or equivalent), but I don't see why re-creating that API would be a blocker to stabilizing the rest of Error. Regardless of what the final backtrace-related access methods look like, they will have the following properties:

  1. They'll need a default no-op implementation in the trait definition, which means that changing/adding/removing backtrace stuff in Nightly should not affect impl Error in Stable.
  2. The types in core cannot reference OS-specific types or logic in std::backtrace, so the universe of possible method signatures added to core::error::Error is limited to those with some sort of indirection through an opaque type in core. If that type can't be referenced or constructed in Stable then it can't affect the API.

For example, various equivalent-but-alternate designs for stack trace access would be API-compatible with the stable parts of the core::error::Error API despite looking completely different in the unstable parts:

pub trait Error: Debug + Display {
    // stable
    fn source(&self) -> Option<&(dyn Error + 'static)> { ... }

    // stable + deprecated
    fn description(&self) -> &str { ... }
    fn cause(&self) -> Option<&dyn Error> { ... }

    // from the current definition of core::error::Error
    //
    // unstable, obvious and trivial default implementation, thus no API risk to Stable
    // even if the rest of `core::error::Error` is stabilized as-is.
    #[unstable]
    fn provide<'a>(&'a self, request: &mut Request<'a>) { }

    // returns an optional opaque handle to a separate #[unstable] metadata type,
    // sort of like the also-in-development DST metadata.
    //
    // changing to this approach from `provide()` would not affect stable API
    #[unstable]
    fn metadata<'a>(&'a self) -> Option<ErrorMetadata<'a>> { None }

    // another approach, where the `Error` implementation pokes things into
    // a provided opaque metadata structure. New methods and APIs could
    // be explored there without touching `Error` at all, stable or unstable.
    #[unstable]
    fn copy_metadata_to(&self, metadata: &mut dyn ErrorMetadata) { }
}
commented

this really needs to be finished