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 RFC 2504, "Fix the Error trait"

Centril opened this issue · comments

This is a tracking issue for the RFC "Fix the Error trait" (rust-lang/rfcs#2504).

Steps:

  • Implement the RFC (cc @rust-lang/libs)
  • Implement proof of concept showing stabilizing backtrace method won't prevent moving Error into core later #77384
  • Fix std::backtrace::Backtrace's fmt representations
    • Precision flag support #65280 this is non-blocking for stabilization
    • Differences with panic! backtraces #71706
  • Adjust documentation (see instructions on forge)
  • Stabilization PR (see instructions on forge)

Unresolved questions:

  • The choice to implement nullability internal to backtrace may prove to be a mistake: during the period when backtrace APIs are only available on nightly, we will gain more experience and possible change backtrace's constructors to return an Option<Backtrace> instead.

Current status:

#53487 (comment)

What parts haven't been implemented?

I've updated the tracking issue to reflect that, but right now the source method has been implemented, but the backtrace related APIs have not been implemented

Perhaps I could try implementing this, is it OK to just use the backtrace crate?

Implementing backtraces in the standard library will probably not use the backtrace crate but rather the backtrace API that's already in the standard library for RUST_BACKTRACE. We can perhaps unify these implementations in the long run, but for now it's infeasible to do so.

The current platform-independent backtrace code lives here, mostly in the _print function. Implementing the API defined in the RFC will likely involve reusing these interfaces to define a struct which carries along the relevant information.

@laaas if you'd like to implement this please feel free! Also feel free to reach out with any questions.

Thanks! Will definitely try implementing it.

Thanks! Will definitely try implementing it.

@laaas Did you make any progress? Need any help?

@frewsxcv Actually not!

I haven't really looked at it much, so if you want to work on it, go ahead!

cc @rust-lang/libs @withoutboats @mitsuhiko would be really nice to make progress here

I would move than love to spearhead this.

@mitsuhiko go go go! if you need any help around the backtrace stuff @alexcrichton can get you set up.

Just an update from my side about why there is no progress: I tried a few things to expose a sensible backtrace object and with the current system that is in place it's all too likely that the outcome is just a bad workaround. I'm currently leaning towards waiting for the backtrace support in rust to be based on backtrace-rs.

Is migration to backtrace-rs is something that is already happening? Any issue to track progress?

That landed ~4 days ago: #60852

commented

Does this RFC also include backtrace printing for ? in tests? Currently, when you switch to ? from unwrap, the backtraces are very poor. The only useful info those backtraces convey (amidst a mountain of spam) is the test that failed along with its line, nothing more.

That sounds more like the "returning result from main" RFC to me (see rust-lang/rfcs#1176 and rust-lang/rfcs#1937).

commented

@mathstuf yeah it's the combination of both features. The "return result from main" RFC has already been merged stabilized but this RFC adds backtraces to the Error trait and standard library which would be needed if you want to print backtraces.

I've posted an initial implementation of backtraces at #64154

It would be nice to have something like Backtrace::was_captured() that returns status() == BacktraceStatus::Captured so we don't have to import that enum.

The lack of PartialEq and Eq impls for BacktraceStatus is also pretty painful.

@abonander

The lack of PartialEq and Eq impls for BacktraceStatus is also pretty painful.

Why? I cannot figure out situation where I should compare multiple backtraces for equality.

commented

@tyranron BacktraceStatus is not a complete backtrace but only a C-like (non-ADT) enum with three variants.

@est31 oh... misread that 😵. Thanks for explanation!

@tyranron There's currently no way to check if a backtrace was captured besides

let was_captured = match backtrace.status() {
    BacktraceStatus::Captured => true,
    _ => false,
};

which seems like a significant oversight.

I would gladly r+ a PR adding Eq and PartialEq to BacktraceStatus.

I know that exposing frames is not yet an option, but I wonder if we could add a method or function somewhere that lets one collect at least the IPs into a vector from a backtrace. Right now I cannot use this backtrace object without regex parsing the debug output.

Is there a reason that couldn't be a future RFC? That is, isn't it just additional API and types on top of Backtrace and nothing that's a fundamental redesign (of its public API)? I'd rather see the Error trait get fixed earlier rather than later so that crates can start using it. I've had a number of "improve error handling" PRs held up on this RFC already.

No. That should not hold it up.

Can we patch all error types in the stdlib to capture backtraces now? At least io::Error::new if nothing else, I think. The trace will end up in src/sys most of the time but it should still provide a lot of helpful context. Maybe we could figure out how to drop frames from the trace which are just implementation details in the stdlib (the same way panic traces shouldn't show a bunch of frames in the src/panic).

Triage: the remaining unstable APIs pointing here are the std::backtrace module (and its contents) as well as the std::error::Error::backtrace trait method. Do these have remaining issues blocking stabilization?

@SimonSapin Mainly just that these APIs were only merged about a month ago. I'd like to get more feedback about them from more users, for example the authors of different error handling crates.

Fair enough

I would like to see the Debug and Display representations for std::backtrace::Backtrace fixed prior to stabilization as described in #65280. I added that to the checklist at the top.

I'm currently working on a Rust library which is mostly intended for use through C FFI. The errors returned by this library include backtraces, but instead of pre-formatting them, the backtraces include all of the relevant information about each stack frame (address, resolved symbol name, symbol file, symbol file line number) so that the caller can format it however they like or include them in their own language-native stackframe error messages.

Unfortunately, the current state of std::backtrace::Backtrace is such that none of this information appears to be available -- you can only format the backtraces as strings. While I understand some of these things might be less-than-pretty to include in std (given the guarantees to keep std stable effectively forever), it really should be at least possible to get an iterator over the list of addresses in the backtrace (that way, the backtrace or other crates could provide the rest of the features I mentioned while using the standard library's Backtrace types).

Let me know if I should open a separate issue for this.

commented

@cyphar I think the stabilization behaviour is similar to proc macros. They too first only had a string based API and only later got a token based one. So std backtraces will likely get that functionality later.

commented

I would like to see the Debug and Display representations for std::backtrace::Backtrace fixed prior to stabilization as described in #65280. I added that to the checklist at the top.

The work to change the formatting has gone stale: rust-lang/backtrace-rs#261

Can't the formatting be improved after backtraces have become stable? I'd love to see stabilization in 1.42.0. In any case It'll take some time until all libraries integrate backtraces into their errors (including std).

@est31 I am going to stick to getting those formatting changes made before stabilization. Printing the backtrace is the core thing being stabilized in the first round of backtrace stabilizations; it should be a format that has been somewhat thought about before people start building on top of it.

In any case this should be a simple change, someone should just send a different PR.

I would settle for just the first and third bullet in #65280 (comment), using the standard Debug style and removing stack backtrace:\n from Display. The precision thing can happen later.

commented

Sadly the backtrace library lives in the rust-lang org. The time where I sent gratis PRs to the rust-lang org (or any org that's part of the official Rust project) is over now so I won't be able to make a PR. It's up for grabs!

@dtolnay i would generally recommend not to change the Debug formatting after stabilization. Reason being that parsing that output is currently the only way to customize it and I expect a lot of code to start doing that. I know we're already doing this because failure also did not expose details of the backtrace.

@dtolnay i would generally recommend not to change the Debug formatting after stabilization. Reason being that parsing that output is currently the only way to customize it and I expect a lot of code to start doing that. I know we're already doing this because failure also did not expose details of the backtrace.

@mitsuhiko Would panicking in case unsupported modifiers are specified be an option until their behavior is settled?

@udoprog not sure I follow.

@mitsuhiko If I read you correctly, you seem to be worried about adding behavior to debug formatting modifiers after backtraces and their default formatting has been stabilized. If so, I suggested was to fence these modifiers behind a panic to future proof the implementation, so we can decide on their exact behavior at a later stage without breakage.

So if someone does:

println!("short trace: {:.5}", trace)

Which is one proposal to see just 5 frames, it would panic instead of ignoring it.

Existing code parses the default output. So unless you want to hide backtraces entirely until it's stabilized people (including myself) will start parsing that.

For what it's worth I don't think the built-in stacktrace needs any fancy formatting. Once an API is available to access individual frames all of this can go into an external library.

@mitsuhiko

For what it's worth I don't think the built-in stacktrace needs any fancy formatting. Once an API is available to access individual frames all of this can go into an external library.

FWIW, I completely agree -- it doesn't even need to be a full frame structure that you're providing. You could even just export the instruction pointer for each frame (as an Iterator<usize> or similar) ad then backtrace-ng or backtrace 1.0 could take that and figure out the symbol names and other information from debugging symbols.

The idea of having to parse Debug output in order to reformat stack traces makes me feel quite uneasy. I might just stay on the old versions of backtrace while the work to provide this information is being stabilised.

commented

i would generally recommend not to change the Debug formatting after stabilization. Reason being that parsing that output is currently the only way to customize it and I expect a lot of code to start doing that.

Note that currently the Display impl of Backtrace is the same as the Debug impl, but the two are generally different. Debug has always been meant to output the hidden internals. Its purpose is debugging, not being a stable format. You shouldn't expect it to never change. Display on the other hand has explicitly been been meant for parsing on occasion. E.g. TokenStream. When it was originally stabilized it only had Debug + FromStr implementations (this is the first rustdoc for it, IIRC it took two versions until proc_macro crate rustdocs would be published): https://doc.rust-lang.org/1.17.0/proc_macro/struct.TokenStream.html

Nowadays Debug and Display implementations of TokenStream are explicitly documented, outlining the behaviour of both implementations:

Screenshot_20191230_131721

While the Debug policy is clear, IDK what the general policy on Display is. Whether Display is always meant for parsing, or only on occasion for specific types. Backtrace's Display implementation should be explicitly documented either way.

So, what's the plan for no_std regarding this? In the RFC comments, @withoutboats mentioned "In failure backtrace just always act like the backtrace variable is off when compiled in no_std. We could possibly do the same thing with core someday." but I don't see how this could ever happen since libcore and libstd can't really have different implementations (libstd simply reexports everything from libcore without touching).

Is this the final nail in the coffin for std::error::Error and std::io ever showing up in libcore?

My understanding is that the only blocker for Error in core is that Box is not in core. I'm not sure how feasible it is to improve the language to make it possible for the box methods to be implemented in libstd only. The backtrace itself should be easy enough to have in core.

I would like to give some feedback on the backtrace method. I think it would be better if the default implementation would be changed from always returning None to returning:

self.source().map(|s| s.backtrace())

The reasoning is that usually the backtrace of the source error would be useful as the backtrace of the original error too, or at least more useful than no backtrace at all. Now that it's not stable yet this default implementation can still be changed. If other people agree that this I could make a PR for it.

This was originally discussed here, for automatically generating Error implementations: JelteF/derive_more#110

I strongly disagree. This would make it impossible to figure out of an error holds a backtrace. This would produce duplicate backtraces for rendering entire error chains.

That's a very good point. I can definitely see the problem in the usecase you're discribing. So what you're saying is that it would not only be bad as a default, but implementations that do that should actively be discouraged?

If that's the case I think two things should be done instead of changing the default:

  1. Update the documentation of backtrace explaining that an implementation should not forward to source().backtrace(), but should instead return None.
  2. Add a helper function/method to get the first backtrace in the chain of errors, since the backtrace method should not be used for that.

It absolutely should be discouraged. We already fight with similar issues where an error renders an inner error and then also returns the inner error as source. If you want to render the entire chain of errors you already often see an error as it it was caused by itself.

Okay, so then I completely understand what you mean now. What I'm trying to say is that if this design is not explained clearly people will do it wrong, like they do now with rendering errors inside errors. Especially because most people simply want to render the error message or render the backtrace of an error, not inspect the whole chain manually. So it should be made easy for people to get that result, without messing up their error design.

So I that's why I think it would make sense to have two methods, e.g. backtrace and own_backtrace. One that forwards down and one that doesn't. Otherwise different libraries will implement it inconsistently and mess up both use cases.

Assuming you can see the sense in that, something like that might even be desirable for the displaying of errors. e.g. a function/method that joins all the display values of an error chain with ": ".

So I that's why I think it would make sense to have two methods, e.g. backtrace and own_backtrace. One that forwards down and one that doesn't. Otherwise different libraries will implement it inconsistently and mess up both use cases.

I think the convenience functions are best to be contained elsewhere in a separate crate. If the semantics of the information are good then you can have all these utilities elsewhere easily. Typically an application will only want to render all this information in a single place anyways.

If the semantics of the information are good then you can have all these utilities elsewhere easily.

What I'm saying is that the current API encourages implementing it with the incorrect semantics, if you want to easily get the backtrace of the errors down the line. Like you say what's currently happening already for error display values. So my suggestion is to make the API encourage implementing it with correct semantics.

Given that you dislike the the current state where errors contain their source error messages as well, I'd think you would want the API for backtraces to discourage that.

Typically an application will only want to render all this information in a single place anyways.

I agree for big applications. However, for small ones or during testing, it seems quite normal to me to do something like:

eprintln!("{}\n{}", error, error.last_backtrace()))

From where I'm standing this is not an issue with the API but lack of clear directions by the docs. The docs would also have to discourage the mindless MyErrorEnum::Io(std::io::Error) idiom for similar reasons and we can't prevent this by API.

From where I'm standing this is not an issue with the API but lack of clear directions by the docs.

I guess we simply disagree on that point then, but I definetly agree that at least the docs should be updated.

The docs would also have to discourage the mindless MyErrorEnum::Io(std::io::Error) idiom for similar reasons and we can't prevent this by API.

Unerlated, but could you expand a bit more on that. Do you mean that instead of MyErrorEnum::Io(std::io::Error) it should be e.g. MyErrorEnum::ConnectionError(std::io::Error)? Or do you mean something else?

Unerlated, but could you expand a bit more on that. Do you mean that instead of MyErrorEnum::Io(std::io::Error) it should be e.g.

Correct. Because that way a generic error printer can render out something like

error: Connection error (could not connect to foo)
  caused by: io error (ECONNRESET)

instead of

error: io error (io error (ECONNRESET))
  caused by: io error (ECONNRESET)

Which is what I'm observing frustratingly commonly.

Which is what I'm observing frustratingly commonly.

Without a way to easily make the former in the standard library, the latter ends up being common so that {:?} formatting Just Works™.

@mitsuhiko but that approach requires a treat in the source impl for each entry in the error enum, isn't it?

Since failure is now officially deprecated it again puts many of us into the situation where we have no backtraces on errors on stable if we were to move away from it.

This brings new attention to this issue of stabilizing the backtrace in std.

@mitsuhiko fwiw you can still get backtraces on stable via eyre

I'm probably should have done this a while ago but I'll go ahead and quickly toss together a custom context thats identical to the DefaultContext in eyre except that it captures a backtrace::Backtrace instead of a std::backtrace::Backtrace

@yaahc for a while some libraries were written to use a Fail instead of an Error in the public interface. I'm not super stoked about moving from Fail to another crate for a few versions just to ultimately land in std again.

I think all of this is pretty okay for applications but it's unclear what library authors should do now which used failure.

This isn't really analogous to the Fail trait, eyre is just a fork of anyhow but instead of anyhow::Error it's eyre::Report<C> where C defaults to std::backtrace::Backtrace but can be overridden to gather any form of context.

It's not exactly drop in compatible but I'm planning on fixing that mostly, I can do that today by exporting #[doc(hidden)] versions of all the functions that ended up being renamed. It wouldn't be perfect because there are some situations where type inference breaks for eyre but not anyhow but it shouldn't be particularly burdensome to swap between the two error reporting libraries.

@yaahc right. The question is where does a library migrate to which currently has Fail in the public API. We have quite a few of these at the moment because backtrace are crucial for debugging code in production.

Yea, ideally I'd say anyhow but we have to stabilize this, which is why I brought up eyre. It wasn't to assert that we shouldn't stabilize this as soon as possible.

One topic related to all of this which was asked about in 2018 but that doesn't seem to have been addressed since is no_std support, either via the core or alloc crates. The Error trait is currently only available with std, but the details of this issue might interfere with changing that in the future.

Possibly relevant links:

to add to @fintelia's comment, if Backtrace support in the std::error::Error trait is a blocker on moving the trait to alloc or core this RFC might offer a path removing the backtrace fn from Error without removing the ability to access backtraces.

rust-lang/rfcs#2895

@yaahc Nice! That is a great solution to this issue.

The alloc crate cannot have any dependencies on OS-specific crates such as libc or libunwind, which would be required by backtrace.

Could there be a way to still have the Backtrace type but not pull in the libraries to actually collect backtraces (just returning empty backtraces)? The type itself doesn't really need anything OS-specific, it basically just amounts to:

enum Backtrace {
    Empty,
    Raw(Vec<usize>),
    Resolved(Vec<(String, u32, u32)>), // (file, line, column)
}
enum Backtrace {
    Empty,
    Raw(Vec<usize>),
    Resolved(Vec<(String, u32, u32)>), // (file, line, column)
}

I'd prefer something like Option<Backtrace>. Users are used to using Option and Backtrace::empty() is basically the same.

enum Backtrace {
    Empty,
    Raw(Vec<usize>),
    Resolved(Vec<(String, u32, u32)>), // (file, line, column)
}

This proposal wouldn't allow Backtrace in libcore though. It would allow it in liballoc, but that's really not ideal. Error really belongs in libcore.

One thing that might be possible is to have a type parameter on Error which determines the kind of backtrace support, then exporting a type alias in libstd that sets the type parameter to a specific type. That way, people can still use the existing Error trait alias in libstd world whereas people can opt into implementing in a backtrace-generic way to allow libcore support.

Error has always been unable to move into core due to coherence issues - the addition of Backtrace doesn't change that.

Error has always been unable to move into core due to coherence issues - the addition of Backtrace doesn't change that.

What coherence issues and could said coherence issues theoretically be solved between negative impls and specialization? It seems like adding backtrace to the Error trait's API would act as an impediment down the line.

My understanding is the coherence issue concerns the downcast method implemented on dyn Error: https://doc.rust-lang.org/stable/std/error/trait.Error.html#method.downcast

Basically, dyn Error has a function that depends on Box. This means Error cannot move to libcore - at best it can move to liballoc.

Has the lang team (cc @rust-lang/lang) brainstormed any potential solutions to this problem? Including, but perhaps not limited to, language changes that specifically permit the Error trait to be treated as a special case so that it can be defined in core?

The coherence issues for Box seem like something that's already been solved -- for example, we used to have SliceExt and StrExt because those types could only have methods in liballoc xor libcore, until it was messed with in compiler land so that could be done anyway. Dunno why a similar exception couldn't be made for Box.

So, specifically, we have the mechanism of "inherent impls" that are only accessible from within the standard library. I think what we need here is not an exception for Box but rather an exception for dyn Error, so that you can create a custom inherent impl for dyn Error in the standard library that adds a downcast method. I'm fine with that, though I think it's the kind of thing I would hope to "truly solve" by the work on unifying the core-std split into something with feature gates (I have no idea how feasible that work truly is).

I'd also prefer to handle that via a core/std merge rather than adding more coherence hacks.

I'd also prefer to handle that via a core/std merge rather than adding more coherence hacks.

Would it still be possible to expose Error in core/std when alloc is disabled if Backtrace is part of its API? like, would there be conditional compilation where some functions are only part of the trait when using the full verison of std?

Yeah. The idea would be that core/alloc/std/etc would all just be combined into one std crate, and then there would be Cargo features controlling the existence of things like Error::backtrace, std::alloc, etc.

commented

Right now std uses a bunch of crates from crates.io like hashbrown, cfg-if, libc, etc. that are all #![no_std] but not #![no_core]. How could they be both used by and rely on a unified core/std crate?

Yeah. The idea would be that core/alloc/std/etc would all just be combined into one std crate, and then there would be Cargo features controlling the existence of things like Error::backtrace, std::alloc, etc.

Questions:

  • How far off is this in theory?
  • Has there been an RFC for this kinda thing yet?
  • Would it be possible to do the inherent dyn Error short term to get Error into core and then merge it all together with the eventual theoretical merger down the line without breaking backwards compatibility?

I'm mostly just worried about making Error unavail for no_std ppl for years, so if we can find a path to making the Error trait universal asap we should pursue it, though not at the cost of better solutions in the future.

@est31 This is getting a bit off topic, but we could e.g. directly pull them in as modules rather than crates like we do right now with stdarch iirc.

@yaahc There hasn't been an RFC, just informal discussions at past Rust all-hands; there are probably notes somewhere on a Dropbox Paper. It would be a fairly significant move but I think doable. Moving Error into core would directly prevent the implementation of the approved RFC that this issue tracks. Error has already been unavailable to no_std people for years - that's the status quo!

@sfackler yea, thats kinda what I'm aiming for, I think the right approach is to remove the backtrace fn part of this rfc, stabilize Backtrace, source is already stable, and then implement this rfc which provides the same functionality as backtrace but generalized which then lets us move this to core immediately without making Backtrace part of Errors API. And i know ppl are already dealing with no_std not having Error but the impression that I get from people is that they still wish it did.

commented

Although a core/alloc/std merge has never been explicitly RFC'd, at least half of https://github.com/rust-lang/rfcs/blob/master/text/1868-portability-lint.md is very heavily implying that's the direction we want to go in. But it remains true that this is not likely to hit stable in the forseeable future, and shorter-term solutions for this are likely worthwhile.

Given a core+std merge defining the Backtrace type unconditionally is relatively easy and 99% already implemented

pub struct Backtrace { inner: Inner };
enum Inner {
    Unsupported,
    Disabled,
    #[cfg(std)]
    Captured(Mutex<Capture>),
}

Without the merge it's a little harder but still possible, using "inherent impls" and type erasing the capture

pub struct Backtrace { inner: Inner };

enum Inner {
    Unsupported,
    Disabled,
    Captured(*const ()),
}

// in std
impl Backtrace {
    // the existing pub fn

    fn create() {
        ...
        let inner = Inner::Captured(Box::into_raw(Box::new(capture)) as *const ());
        ...
    }

    fn as_capture(&self) -> Option<&Mutex<Capture>> {
        if let Inner::Capture(ptr) = self {
            Some(unsafe { &*(ptr as *const _) })
        } else {
            None
        }
    }
}

Sure, but if we just use the generic member access logic in the Error trait instead of backtrace we don't even have to change the definition of Backtrace just to make it legal to reference it in core and we can move forward with stabilizing Backtrace as implemented.

@Nemo157 but that would cause coherence issues? Or are you suggesting adding a coherence exception to Backtrace as well?

Yes, that would rely on Backtrace using the same sort of coherence exception to add new inherent methods to it. I also just realised it would need an exception to add Drop to it as well, which is probably much more of a problem.

Although a core/alloc/std merge has never been explicitly RFC'd, at least half of https://github.com/rust-lang/rfcs/blob/master/text/1868-portability-lint.md is very heavily implying that's the direction we want to go in. But it remains true that this is not likely to hit stable in the forseeable future, and shorter-term solutions for this are likely worthwhile.

This is why I brought up the stuff with StrExt and SliceExt; @SimonSapin was tired of the fact that these traits existed due to a limitation in the compiler and because it was a long time before any merge of core, alloc, and std was going to happen, he just did it by adding an unstable compiler feature. Don't think it's that bad of an idea to do this for Error too -- it seems relatively easy to do and relatively easy to undo once the merger takes place, assuming that people use it responsibly.

I'm using failure in production and we rely on the backtrace feature to get useful backtraces for errors (we also use sentry for error reporting). However, parts of the Rust ecosystem have already started switching to anyhow, and this is a big problem when backtraces are not yet stable!

The fn backtrace() feature should be stabilized ASAP, or else we (and other production users) will be stuck without decent error reporting, or unable to upgrade our dependencies for a long period of time. IMO, conditioning this on new language features like rust-lang/rfcs#2895 is not a reasonable solution.

I don't think this makes it more difficult to move Error to core either: there are tons of ways we could solve this down the line, and we don't need to decide on one before stabilizing the backtrace feature.

(For example, we could just move the Backtrace type to core too, and use compiler magic to allow libstd to override it, or have a system similar to how the global allocator can be replaced, there are loads of options)

@Diggsey this isn't an good justification for stablizing fn backtrace, if you need backtraces on stable use eyre+stable-eyre. Eyre is a fork of anyhow so it has all the same features but it doesn't hard code what kind of Backtrace or error report context you use which let's you switch it back to backtrace:: Backtrace which is what stable-eyre does.

How is this not a good justification? eyre doesn't help because our dependencies are moving to anyhow, not eyre. You're blocking a feature that would be useful today on an RFC that may never get merged, a feature that may never be implemented (associated type defaults) all for a requirement (move Error to core) that is already not possible without further changes, all while ignoring the fact that your RFC, as much as I'd like to see it merged, is a sledgehammer to a problem that (should we even need to solve it) is easily solvable using more conventional means.

You're blocking a feature that would be useful today on an RFC that may never get merged, a feature that may never be implemented (associated type defaults) all for a requirement (move Error to core) that is already not possible without further changes

Associated type defaults are not involved in the RFC at all to my knowledge. Also, for the record, I'm not blocking anything, I don't have more power than anyone else here, I don't get a vote in whether or not anything here gets stabilized, I'm just an advocate same as you.

all while ignoring the fact that your RFC, as much as I'd like to see it merged, is a sledgehammer to a problem that (should we even need to solve it) is easily solvable using more conventional means.

I feel like this is a pretty rude characterization of my work >_>. Also if you have more conventional alternatives that support SpanTrace, no_std, and error return traces I'd love to hear them.


If we can take a step back, can I ask why you need fn backtrace in particular? Are you specifically trying to extract backtraces out of anyhow::Error objects to be propagated through the error trait or is the issue that you need backtraces on stable? Because we could stabilize std::backtrace::Backtrace without stabilizing fn backtrace, and you can then use anyhow's own fn backtrace for extracting the backtrace if you need to access it directly when reporting errors to sentry. Would this be sufficient?

How is this not a good justification?

I think overall I'm just worried about prioritizing short term gain in features over long term gain. I'm sympathetic to your needs but yours aren't the only ones, there is a pretty large crowd that wants the Error trait in core that I'm trying to help as well. I don't see how we could get Error into core after stabilizing fn backtrace other than merging core and std, which doesn't seem like something that will happen short term if ever, the downcast issue seems trivial in comparison. So effectively stabilizing fn backtrace could permanently block a feature that is in pretty high demand seemingly in exchange for getting another feature a few months early that we would have gotten eventually if we waited. It just doesn't seem like a good trade off to me.

@yaahc

Because we could stabilize std::backtrace::Backtrace without stabilizing fn backtrace, and you can then use anyhow's own fn backtrace for extracting the backtrace if you need to access it directly when reporting errors to sentry. Would this be sufficient?

This seems like a great idea to me. (Bonus if we can have appropriate mechanisms to control whether errors get backtraces taken or not, given the cost and the size complexity.)

Anyhow does not support backtraces without the backtrace feature, which only works on nightly Rust. It depends on both the std Backtrace type and the Error::backtrace method, because it uses the Error::backtrace method to determine if a backtrace needs to be generated when constructing an anyhow::Error. Maybe @dtolnay would be willing to make anyhow generate a redundant backtrace on stable, but this would be a degredation in the quality of the anyhow crate.

Backtrace does not block moving Error into core because its so much easier to solve than the coherence issue (which I am personally unhappy to see "solved" with more hacks on our coherence system instead of solving the underyling issue). We could move backtrace into core by creating hooks of some kind for std to provide the actual backtrace behavior, and without those hooks implemented it would do nothing, just like we have working for panicking, just like we would require to add hooks for std to add a special inherent impl for dyn Error. Stabilizing Error::backtrace should not be blocked on Backtrace being core.

We are not prioritizing the short term over the long term by stabilizing a method that's been in development for nearly 3 years (from when it first appeared in failure). If the Backtrace type is ready to stabilize, so is Error::backtrace.

I've created a stabilization proposal here: #72981. I've tried to cover all of the issues discussed. Other people who have concerns about stabilizing backtrace should leave comments on this PR!

One minor nit: Perhaps RUST_STDLIB_BACKTRACE would be better than RUST_LIB_BACKTRACE. When I first saw this I found myself asking the question "is this something library authors are supposed to honor or otherwise deal with". I'm pretty sure the answer is "no" and the suggested name makes that more obvious.