Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upFix the Error trait #2504
Conversation
withoutboats
added
the
T-libs
label
Jul 19, 2018
This comment has been minimized.
This comment has been minimized.
|
Alternative to
|
This comment has been minimized.
This comment has been minimized.
algesten
commented
Jul 19, 2018
|
Or
|
This comment has been minimized.
This comment has been minimized.
|
Has |
This comment has been minimized.
This comment has been minimized.
ben0x539
commented
Jul 19, 2018
|
I'm gonna be extremely sad if we end up with an awkward synonym for |
This comment has been minimized.
This comment has been minimized.
|
I don’t want to chime in too much on the bikeshedding but with regards to naming here is what others do:
Cause is the most common but obiously not available. For the non cause uses they are all different. However one other thing that comes to mind is that since we are already fixing this it might be reasonable to consider aggregate exceptions as well. A fanout can collect multiple errors that might make sense to capture. In that case the name |
This comment has been minimized.
This comment has been minimized.
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.
Not sure what you were thinking but |
This comment has been minimized.
This comment has been minimized.
epage
commented
Jul 19, 2018
fn backtrace(&self) -> Option<&Backtrace> {
self.source().map(|s| s.backtrace())
}
This is more hypothetical. I am not completely sure on motivations. Possible motivations:
Possible ways of doing this
I can't remember how it was good for |
tmandry
reviewed
Jul 19, 2018
|
|
||
| Today, the `RUST_BACKTRACE` controls backtraces generated by panics. After this | ||
| RFC, it also controls backtraces generated in the standard library: no backtrace | ||
| will be generated when calling `Backtrace::new` unless this variable is set. |
This comment has been minimized.
This comment has been minimized.
tmandry
Jul 19, 2018
Does this mean Backtrace::new should return an Option, or that it will simply not be called by any code inside std (and always produces a backtrace when called)?
This comment has been minimized.
This comment has been minimized.
For what reason was
Given that the text makes a prior distinction "end-user display message" and "programmer debug message", I think the quoted section intends to suggest that backtraces are for programmers rather than end-users, yes? |
sfackler
reviewed
Jul 19, 2018
|
|
||
| Today, the `RUST_BACKTRACE` controls backtraces generated by panics. After this | ||
| RFC, it also controls backtraces generated in the standard library: no backtrace | ||
| will be generated when calling `Backtrace::new` unless this variable is set. |
This comment has been minimized.
This comment has been minimized.
sfackler
Jul 19, 2018
Member
It seems kind of weird for it to be impossible to get a backtrace at all if the right environment variable isn't set. It seems reasonable to have a constructor that returns an Option<Backtrace> by checking the environment, but I think Backtrace::new should just unconditionally produce a backtrace.
This comment has been minimized.
This comment has been minimized.
mitsuhiko
Jul 19, 2018
Contributor
I agree. I effectively have to force this envvar on all the time now on startup which seems like a weird way to control the system.
This comment has been minimized.
This comment has been minimized.
epage
Jul 19, 2018
the behavior of a Backtrace is based on whether or not that environment variable is set.
Personally, I feel like that policy should be decoupled from being able to get a Backtrace. We should have an aligned default policy but we should make it possible for people to create policies to suit their needs. For example, this RFC is bringing up a change in policy:
From RFC
Two additional variables will be added: RUST_PANIC_BACKTRACE and RUST_STD_BACKTRACE: these will independently override the behavior of RUST_BACKTRACE for backtraces generated for panics and from the std API.
How will Backtrace know its calling context to use these two variables?
If it isn't, its a null type that doesn't collect any backtrace information and prints nothing when displayed.
And in Errors case, is there a reason to use null objects rather than Option like the API?
This comment has been minimized.
This comment has been minimized.
It was an oversight - |
This comment has been minimized.
This comment has been minimized.
|
@tmandry @sfackler To clarify, the way that error-chain and failure both work, |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
I was actually thinking it would return some sort of wrapped I do not completely want to lose track of the aggregation case though because with async code that will come up sooner or later. |
This comment has been minimized.
This comment has been minimized.
|
Along the lines of creating a backtrace, I agree with @sfackler's comment as well! I wonder, would something like this suffice? impl Backtrace {
// always captures a backtrace, unless platform
// does not support it, in which case `None` is returned
pub fn new() -> Option<Backtrace>;
// optionally capture a backtrace, dictated by environment variables
// returns `None` if env vars say to not capture a backtrace
// or on platforms that don't support it
pub fn maybe_new() -> Option<Backtrace>;
}That way if a tool wants to unconditionally capture a backtrace they have a means of doing so. At the same time though we'd provide an idiomatic constructor for crates like |
This comment has been minimized.
This comment has been minimized.
bluetech
commented
Jul 20, 2018
Is it possible to make panic use |
This comment has been minimized.
This comment has been minimized.
|
@alexcrichton agreed with this a whole bunch. I would generally not make Instead of impl Backtrace {
// capture a backtrace unless unsupported.
pub fn capture() -> Option<Backtrace>;
// capture a backtrace if possible and wanted for the given situation.
pub fn capture_for(c: BacktraceConsumer) -> Option<Backtrace>;
}
pub enum BacktraceConsumer {
Panic,
Error,
}(I started a spec for a better backtrace object a while back but never finished it because I ran into limitations with the symbolication support in backtrace.rs that makes the API I envisioned really hard to get. If there is interest I can unearth what I wrote together though and remove those bits). |
This comment has been minimized.
This comment has been minimized.
|
I'm uncertain what the benefit of any
The distinction is an intentional feature; I received a lot of feedback on failure that users want the ability to control the backtraces they get from panics differently from the backtraces created by their libraries, because they can't afford to have backtraces in library code but want to have backtraces when things catastrophically fail. |
This comment has been minimized.
This comment has been minimized.
|
Right now I as a user check against an empty string return from display.
Hardly any better :)
…On Fri, Jul 20, 2018 at 2:06 PM boats ***@***.***> wrote:
I'm uncertain what the benefit of any -> Option<Backtrace> constructor
is. How is a user supposed to handle the None case there? I can't think
of any way to handle it other than carrying an Option<Backtrace>
indefinitely, ultimately checking for the None case when you print it out;
in other words, the same behavior that having nullity be a permitted
backtrace behavior. I also notice that there is no API in which backtrace
is actually present unconditionally; it seems more correct and more user
friendly to incorporate this nullity into the behavior of the backtrace
type instead.
I don't know if it is possible, but if "panic backtraces" and "std
backtraces" were the same thing, that sounds better.
The distinction is an intentional feature; I received a lot of feedback on
failure that users want the ability to control the backtraces they get from
panics differently from the backtraces created by their libraries, because
they can't afford to have backtraces in library code but want to have
backtraces when things catastrophically fail.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#2504 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAAc5NVDzzXACXD8TydHh-OLep7KACBoks5uIcfagaJpZM4VWUn_>
.
|
This comment has been minimized.
This comment has been minimized.
|
@mitsuhiko What do you do when the string is empty? That's what I'm trying to get at: for most use cases, I don't know how you would handle a null backtrace. (I think the better solution would be to have an API on backtrace that tells you if its null or not.) |
This comment has been minimized.
This comment has been minimized.
|
@withoutboats in the trivial case i render differently (eg: i render out "stacktrace:\n{}" vs "set the envvar to get stacktraces). More importantly though I currently also need to regex parse the backtrace since there is no api to access so there is more to this. With a backtrace API I could also see that I did not get any frames. For me the main difference between |
This comment has been minimized.
This comment has been minimized.
I don't know what you mean by "no backtrace was requested," but I'm having trouble understanding how this comment squares with the API you proposed above, in which both constructors return an |
This comment has been minimized.
This comment has been minimized.
The usecase that comes up for me is that right now some APIs (like panic handlers, or errors with stacktraces bubbled up to main) can show backtraces to the user but only if the envvar is set. So I want to hint to the user when to turn it on. Not requested was that the envvar was not set.
It would maybe make sense to have the |
This comment has been minimized.
This comment has been minimized.
|
How about: enum Reason {
Envvar,
NotSupported,
}
enum Backtrace {
Captured(RealBacktrace),
NotCaptured(Reason)
Synthetic
}
impl Default for Backtrace {
fn default() {
Backtrace::Synthetic
}
}
impl Backtrace {
fn capture() -> Backtrace() {
unimplemented!("actually capture a backtrace here, NotSupported if not supported")
}
fn new() -> Backtrace {
match envvar {
true => Backtrace::capture(),
false => Backtrace::NotCaptured(Reason::Envvar),
}
}
}Then any user can place the desired printing on top of that. |
This comment has been minimized.
This comment has been minimized.
|
An example of why including the nullity in the backtrace is useful: if we don't, presumably errors will be storing I think it makes sense for backtrace to report its status somehow; I initially imagined a boolean, but probably we want it to be to give more information, that is, something like: #[non_exhaustive]
enum BacktraceStatus {
Captured,
Disabled,
Unsupported,
}
impl Backtrace {
pub fn status(&self) -> BacktraceStatus
}You could match on this status to convey to users, e.g. "turn the env var on," or "backtraces are unsupported on this platform" |
This comment has been minimized.
This comment has been minimized.
epage
commented
Jul 20, 2018
|
@withoutboats how are you planning on handling this requirement:
I have some thoughts on the API but I feel like the answer to the above might also help shape it and want to make sure its included. |
joshtriplett
reviewed
Jul 20, 2018
| methods will be encouraged (by warnings) to change their code, and library | ||
| authors will need to revisit whether they should override one of the new | ||
| methods. | ||
|
|
This comment has been minimized.
This comment has been minimized.
joshtriplett
Jul 20, 2018
Member
Under "Drawbacks", could you please explicitly document whether there's any functionality drawback to adding the 'static bound on source? Is there any error pattern that that would prevent? And if not, could you state that explicitly?
This comment has been minimized.
This comment has been minimized.
|
@GuillaumeGomez i don't see how that is relevant to this RFC since it's just a trait method. Only the implementor could potentially incur performance issues and that is already the case since many errors carry backtraces (all of error chain and failure). |
This comment has been minimized.
This comment has been minimized.
plietar
commented
Aug 15, 2018
•
The situation with two trait Error: Display + Debug {
fn r#2018cause(&self) -> Option<&dyn Error + 'static> { None }
fn r#2015cause(&self) -> Option<&dyn Error> { self.r#2018cause() }
}Calling |
This comment has been minimized.
This comment has been minimized.
Yes and I (strong personal opinion in here) don't like the current error handling crates. They all add an overhead that I find in most cases heavy and useless. But it remains a choice to use them or not. With this trait, you don't have the choice anymore. |
This comment has been minimized.
This comment has been minimized.
|
@GuillaumeGomez "With this trait, you don't have the choice anymore". I do not see how this trait changes anything. You have the same choice now as you had before. |
This comment has been minimized.
This comment has been minimized.
dekellum
commented
Aug 15, 2018
|
Please reviewers, bias toward action (merging this) unless you find substantive compatibility problems not covered in The transition plan section of this RFC. |
This comment has been minimized.
This comment has been minimized.
|
Does creating a backtrace allocate? I suppose it might allocate a String but then Error probably cannot be used anywhere close to an allocator or one must be careful doing so. This limitation is ok, but I am not sure what the scope of this limitation is. Also, Backtrace contains a backtrace to what exactly? Where is the “leaf” stack frame of this backtrace? Is it some implementation detail of the Backtrace machinery? The stack frame of the caller method? Platform / optimization dependent (e.g. inlining)? I’d expect that for the backtrace to be useful it must point to the caller stackframe and all stackframes from the Backtrace implementation must have been pruned, but I haven’t found any information about this in the RFC or the comments. Sorry if this was already asked or discussed. |
This comment has been minimized.
This comment has been minimized.
|
@gnzlbg @GuillaumeGomez This RFC proposes to add this default method to the trait fn backtrace(&self) -> Option<&Backtrace> {
None
}Any error which doesn't include a backtrace just doesn't override this method; in other words, nothing about that experience changes. What has changed is that we have created a standardized API for accessing the backtrace of an error which does have one. |
This comment has been minimized.
This comment has been minimized.
|
I'm just afraid that on the opposite to std lib, you won't be able to disable backtraces when using crates. Maybe I'm just worrying about things that won't happen but still... |
This comment has been minimized.
This comment has been minimized.
|
There is no difference to today. There are already crates that carry backtraces. |
This comment has been minimized.
This comment has been minimized.
DDOtten
commented
Aug 18, 2018
Small tweak: I would take |
This comment has been minimized.
This comment has been minimized.
And this is part of why I don't use them. |
This comment has been minimized.
This comment has been minimized.
rfcbot
commented
Aug 19, 2018
|
The final comment period, with a disposition to merge, as per the review above, is now complete. |
rfcbot
added
finished-final-comment-period
and removed
final-comment-period
labels
Aug 19, 2018
Centril
referenced this pull request
Aug 19, 2018
Open
Tracking issue for RFC 2504, "Fix the Error trait" #53487
Centril
merged commit 5d4b752
into
master
Aug 19, 2018
This comment has been minimized.
This comment has been minimized.
|
Huzzah! This RFC has been merged! Tracking issue: rust-lang/rust#53487 |
This comment has been minimized.
This comment has been minimized.
From the first line of the RFC:
In particular, this RFC adds an API, My two questions are:
The RFC doesn't even say what a backtrace is, and when it states: "Capture the backtrace for the current stack if it is supported on this platform" I think it is important to state in which state the current stack is when its backtrace is captured. E.g. if I write: fn foo() {
let b = Backtrace::capture();
println!("{}", b);
}what am I going to see ? ...
...
fooor ...
...
foo
Backtrace::capture
Backtrace::barbaz
os::raw:..
...? I find the meaning of:
unclear. What does "user display purposes" mean ? The answers to all these questions are the difference between getting a backtrace that I can just pass to panic to abort a process, or whether I can actually parse the backtrace to do something useful. |
withoutboats commentedJul 19, 2018
•
edited by Centril
Rendered
Tracking issue