Stream: t-lang

Topic: Error context, with_context, and common contexts


Josh Triplett (Apr 02 2020 at 21:42, on Zulip):

I've repeatedly found, when using anyhow in a large project, that I really want the same generated context across various uses of ?, and I found it painful to repeatedly write things like .with_context(|| format!("thing {} detail {}", thing, detail))?. To the point that in some cases I found myself writing let context = || format!("thing {} detail {}", thing, detail); at the top of a function and repeatedly using .with_context(context)?, which then led me to forget what was in the context and almost use the wrong context for an error. I also find myself only using it on errors that "need" context to make sense, when I really want it on most errors.

Josh Triplett (Apr 02 2020 at 21:43, on Zulip):

I know that some error-handling libraries have experimented with having function-level contexts.

Josh Triplett (Apr 02 2020 at 21:44, on Zulip):

I find myself wishing for a kind of "context block", or some kind of similar information, that lets me say "all errors in this block are related to X", and "all errors in this inner block are also related to Y", and have that automatically result in .with_context calls or similar, ideally with no overhead when not generating an error.

Josh Triplett (Apr 02 2020 at 21:48, on Zulip):

I don't know what the language construct would look like to support that. I'm tempted to say that a combination of try and a macro might get part of the way there.

Josh Triplett (Apr 02 2020 at 21:49, on Zulip):

I could, for instance, imagine something like error_context!(|| format!("thing {} detail {}", thing, detail)) { ... }

Josh Triplett (Apr 02 2020 at 21:50, on Zulip):

But I also, slightly, wonder if it'd be a terrible idea to support some kind of "please wrap the remainder of my current block in this macro without making me nest braces around it" mechanism.

Josh Triplett (Apr 02 2020 at 21:50, on Zulip):

(That would also likely produce better results with rustfmt.)

Josh Triplett (Apr 02 2020 at 21:55, on Zulip):

I also feel like "context" and "backtrace" are a little conflated. What I'm providing isn't another error, it's context for the error that's already happening.

Josh Triplett (Apr 02 2020 at 21:55, on Zulip):

I think there's a semantic difference between "Error X happened while I was doing Y" and "Error Y happened, the underlying error is X but you may not care about that".

Josh Triplett (Apr 02 2020 at 22:13, on Zulip):

cc @Jane Lusby

Jane Lusby (Apr 02 2020 at 22:17, on Zulip):

I also feel like "context" and "backtrace" are a little conflated

Jane Lusby (Apr 02 2020 at 22:17, on Zulip):

you're speaking my language

Jane Lusby (Apr 02 2020 at 22:17, on Zulip):

disclaimer, in eyre which is a fork of anyhow I renamed the context fn to wrap_err

Jane Lusby (Apr 02 2020 at 22:18, on Zulip):

can I as what kind of context you're trying to provide? if its just runtime info I would argue that the right solution is tracing_error::SpanTrace

Jane Lusby (Apr 02 2020 at 22:19, on Zulip):

or something similar

Jane Lusby (Apr 02 2020 at 22:19, on Zulip):

but the idea is you wrap a scope with a span that has thing and detail as fields

Jane Lusby (Apr 02 2020 at 22:20, on Zulip):

and then any errors that are created in that scope that also capture a SpanTrace will get those fields in the spantrace at the end

Jane Lusby (Apr 02 2020 at 22:20, on Zulip):
   0: custom_error::do_another_thing
        with answer=42 will_succeed=false
          at examples/examples/custom_error.rs:42
   1: custom_error::do_something
        with foo="hello world"
          at examples/examples/custom_error.rs:37
Jane Lusby (Apr 02 2020 at 22:21, on Zulip):

because it sounds like you're inserting members that you want to include with an error that don't themselves represent a new error and you're doing it by creating a new error to wrap the existing one with the context fn

Jane Lusby (Apr 02 2020 at 22:21, on Zulip):

lmk if this is accurate or not @Josh Triplett

Josh Triplett (Apr 02 2020 at 22:25, on Zulip):

(One moment, interruption, will answer shortly with examples.)

Josh Triplett (Apr 02 2020 at 22:43, on Zulip):

@Jane Lusby Not sure what you mean by "runtime info".

Jane Lusby (Apr 02 2020 at 22:44, on Zulip):

like, local members that you want to see the values of at the time the error was created

Jane Lusby (Apr 02 2020 at 22:44, on Zulip):

but not necessarily stuff that is part of the error message

Josh Triplett (Apr 02 2020 at 22:44, on Zulip):

So, a couple of things...

Josh Triplett (Apr 02 2020 at 22:44, on Zulip):

First, this isn't meant as pure "debugging information"; this is information that may be useful to the end-user, not just me as the developer.

Josh Triplett (Apr 02 2020 at 22:44, on Zulip):

I want this error to be vaguely useful to my users.

Josh Triplett (Apr 02 2020 at 22:46, on Zulip):

As an example, I might have an error like "this git blob {hash} doesn't exist in the repository", and the context is let context = || format!("Processing tree {}", tree.id());

Josh Triplett (Apr 02 2020 at 22:46, on Zulip):

Because it's not very useful to say "hash {} didn't exist" without saying that it was the name N in the tree T (and "name N" would be nice to include too here).

Jane Lusby (Apr 02 2020 at 22:47, on Zulip):

okay

simulacrum (Apr 02 2020 at 22:47, on Zulip):

Just wanted to note that I have this frustration every single time I try to have nice errors in Rust. I've not found good solutions yet.

I usually have found that what I sort of want is to be able to say "here's some context" and then everything that happens after that (before that context goes out of scope, maybe?) would include it, sort of like how I think tracing's scopes work

Josh Triplett (Apr 02 2020 at 22:48, on Zulip):

@simulacrum Yes please!

Jane Lusby (Apr 02 2020 at 22:48, on Zulip):

so the this git blob {hash} doesnt exist in the repository is the error message

Jane Lusby (Apr 02 2020 at 22:48, on Zulip):

and Processing tree {} is context

simulacrum (Apr 02 2020 at 22:48, on Zulip):

so, maybe a better example, is that I usually actually want sort of "{hash} doesn't exist" -> "in tree {}" -> "in repository {}" etc

simulacrum (Apr 02 2020 at 22:49, on Zulip):

and usually these are in different functions sort of spread apart quite widely, in my experience

Jane Lusby (Apr 02 2020 at 22:49, on Zulip):

yea I'm still leaning towards tracing being a good fit but the debug_context! stuff would also work

Jane Lusby (Apr 02 2020 at 22:49, on Zulip):

though I dont thikn it would work well with anyhow

Josh Triplett (Apr 02 2020 at 22:49, on Zulip):

@Jane Lusby Right. I'm currently using anyhow, and .with_context(context)?.

Jane Lusby (Apr 02 2020 at 22:49, on Zulip):

because anyhow doesnt allow for adding context that isnt itself an error in your chain of errors

Jane Lusby (Apr 02 2020 at 22:49, on Zulip):

which you could just do, but I think its bad personally

Jane Lusby (Apr 02 2020 at 22:50, on Zulip):

this is the single reason why I forked anyhow

simulacrum (Apr 02 2020 at 22:50, on Zulip):

hm, well, actually, I don't usually care - in almost all cases where I do this it's just going to get printed at the end, I don't have any downcasting etc

Jane Lusby (Apr 02 2020 at 22:50, on Zulip):

https://docs.rs/eyre/0.3.5/src/eyre/lib.rs.html#466-471

simulacrum (Apr 02 2020 at 22:50, on Zulip):

(indeed whenever I start wanting to downcast I usually try to drop the opaque Error structs and go for some enum or something

Jane Lusby (Apr 02 2020 at 22:51, on Zulip):

note, eyre adds a context type parameter

Josh Triplett (Apr 02 2020 at 22:51, on Zulip):

@simulacrum Likewise. If I'm using anyhow it's because the only thing that'll ever happen with an error is reporting it and exiting gracefully.

Jane Lusby (Apr 02 2020 at 22:51, on Zulip):

so you could in theory have debug_context expose he fields in a threadlocal, and then have wrap_err grab that context and add it to the set of contexts in the ErrReport

Josh Triplett (Apr 02 2020 at 22:52, on Zulip):

@Jane Lusby That doesn't sound especially zero-overhead.

centril (Apr 02 2020 at 22:52, on Zulip):

(Small note: I think in general given that there's a lot of "mode du jour" and shifting sands wrt. error handling utility libraries, that makes me pretty skeptical of anything approaching language level changes.)

centril (Apr 02 2020 at 22:52, on Zulip):

(Like... anyhow is a very new library)

Jane Lusby (Apr 02 2020 at 22:52, on Zulip):

^

Jane Lusby (Apr 02 2020 at 22:52, on Zulip):

big agree on that

Josh Triplett (Apr 02 2020 at 22:53, on Zulip):

@centril We're not at that phase now, we're at the brainstorming phase of "what does the thing we want look like".

centril (Apr 02 2020 at 22:53, on Zulip):

@Josh Triplett yeah hence parenthetical :slight_smile:

Jane Lusby (Apr 02 2020 at 22:53, on Zulip):

okay so you want a context

Jane Lusby (Apr 02 2020 at 22:53, on Zulip):

thats available locally

Josh Triplett (Apr 02 2020 at 22:53, on Zulip):

@Jane Lusby So, I'm not excited about the idea of switching error-handling libraries. I'm honestly hopeful that the next time that I switch will be when I switch to std.

Jane Lusby (Apr 02 2020 at 22:53, on Zulip):

okay

simulacrum (Apr 02 2020 at 22:53, on Zulip):

I think the hard part for me is figuring out how to actually associate the context

Jane Lusby (Apr 02 2020 at 22:54, on Zulip):

so that leaves you with inserting the context into the error chain

Josh Triplett (Apr 02 2020 at 22:54, on Zulip):

I'm not saying that I'm completely unwilling to switch at this point, but it would need to be for a reason stronger than "it doesn't feel right for it to be part of the chain of errors". :)

Jane Lusby (Apr 02 2020 at 22:54, on Zulip):

thats fair

Josh Triplett (Apr 02 2020 at 22:54, on Zulip):

I also hope that anyhow might be amenable to introducing a way to do this.

simulacrum (Apr 02 2020 at 22:54, on Zulip):

anyway, need to run, but will catch up later

simulacrum (Apr 02 2020 at 22:54, on Zulip):

maybe write up something more long form...

Jane Lusby (Apr 02 2020 at 22:55, on Zulip):

okay well, lets still see if we cant figure out what we would need to have added to anyhow

Jane Lusby (Apr 02 2020 at 22:56, on Zulip):

so you want to specify the context once

Josh Triplett (Apr 02 2020 at 22:56, on Zulip):

Well, once per new information to the context.

Jane Lusby (Apr 02 2020 at 22:56, on Zulip):

and then have any call within that scope use that context implicitly

Josh Triplett (Apr 02 2020 at 22:56, on Zulip):

Right. Minimum would be "open a scope within which every error gets this context". Bonus if I can also augment the current context without opening a new nested block.

Josh Triplett (Apr 02 2020 at 22:56, on Zulip):

(The difference mostly being indentation, frankly.)

Jane Lusby (Apr 02 2020 at 22:57, on Zulip):

mhmm

Jane Lusby (Apr 02 2020 at 22:57, on Zulip):

and tracing is off the table?

Josh Triplett (Apr 02 2020 at 22:57, on Zulip):

I know very little about tracing.

Jane Lusby (Apr 02 2020 at 22:57, on Zulip):

because this is exactly what tracing-error + tracing is designed to do

Jane Lusby (Apr 02 2020 at 22:57, on Zulip):

so with your specific example

Jane Lusby (Apr 02 2020 at 22:57, on Zulip):

it would look like this vaguely

Josh Triplett (Apr 02 2020 at 22:58, on Zulip):

One piece of information: I'm writing performance-sensitive code, where my inner loops run enough times that a tiny change to them can cost a substantial fraction of my runtime, so it's important that error handling add zero overhead if not erroring.

Josh Triplett (Apr 02 2020 at 22:58, on Zulip):

I don't know enough about tracing or how it works to know if it has that property.

Jane Lusby (Apr 02 2020 at 22:59, on Zulip):

tracing is pretty low overhead but it writes eagerly, tho it does so without allocating most of the time

Jane Lusby (Apr 02 2020 at 22:59, on Zulip):

so each time you encounter a span it has to copy the display data into the registry

Josh Triplett (Apr 02 2020 at 22:59, on Zulip):

Then that's very likely to be too much overhead, unfortunately.

Jane Lusby (Apr 02 2020 at 22:59, on Zulip):

okay so no tracing

Josh Triplett (Apr 02 2020 at 23:00, on Zulip):

At least not unless there's a variant of tracing that doesn't have that property, yeah. Sorry.

Jane Lusby (Apr 02 2020 at 23:00, on Zulip):

not that I know of but eliza is more of an expert

Jane Lusby (Apr 02 2020 at 23:00, on Zulip):

@David Barsky has more experience using tracing in perf critical code than me

Jane Lusby (Apr 02 2020 at 23:00, on Zulip):

he can probably also chime in

Jane Lusby (Apr 02 2020 at 23:01, on Zulip):

so I dont know how you would pass it implicitly

Jane Lusby (Apr 02 2020 at 23:01, on Zulip):

without using thread locals

Josh Triplett (Apr 02 2020 at 23:01, on Zulip):

As a random example, I was curious how much difference .with_context(|| format!(...)) versus .context(format!(...)) made, and the answer turned out to be "3-4% of my total runtime".

Jane Lusby (Apr 02 2020 at 23:01, on Zulip):

nicee

Jane Lusby (Apr 02 2020 at 23:01, on Zulip):

but tracing will probably be a lot less than that

Jane Lusby (Apr 02 2020 at 23:01, on Zulip):

because format involves allocations that tracing wont

Jane Lusby (Apr 02 2020 at 23:01, on Zulip):

it might be easy enough to benchmark

Josh Triplett (Apr 02 2020 at 23:01, on Zulip):

Fair.

Josh Triplett (Apr 02 2020 at 23:02, on Zulip):

Quite possibly worth the experiment.

Jane Lusby (Apr 02 2020 at 23:02, on Zulip):

you can just setup a subscriber, throw aspan there, capture a spantrace and immediately discard it and see what the perf impact is

Jane Lusby (Apr 02 2020 at 23:02, on Zulip):

well you probably wouldnt want to capture the spantrace

Jane Lusby (Apr 02 2020 at 23:03, on Zulip):

or only capture it and discard it inside of one of the closures that only happen on the error path

Jane Lusby (Apr 02 2020 at 23:03, on Zulip):

anyways

Jane Lusby (Apr 02 2020 at 23:03, on Zulip):

i feel like you can get pretty close to what you want with format_args! and context maybe

Josh Triplett (Apr 02 2020 at 23:03, on Zulip):

Jane Lusby said:

so I dont know how you would pass it implicitly

Jane Lusby said:

without using thread locals

Assume for the moment that we had try blocks. Then I would hope for something that's effectively try { ... }.with_context(|| ...)? wrapped in a macro.

Jane Lusby (Apr 02 2020 at 23:03, on Zulip):

aah yea that would work

Josh Triplett (Apr 02 2020 at 23:04, on Zulip):

With the context first rather than last.

Jane Lusby (Apr 02 2020 at 23:04, on Zulip):

format_args is evaluated lazily right?

Jane Lusby (Apr 02 2020 at 23:04, on Zulip):

does it impl display

Josh Triplett (Apr 02 2020 at 23:04, on Zulip):

I don't know the internals of the format machinery.

Jane Lusby (Apr 02 2020 at 23:05, on Zulip):

it does impl display

Jane Lusby (Apr 02 2020 at 23:05, on Zulip):

oh but you cant use it as context

Jane Lusby (Apr 02 2020 at 23:05, on Zulip):

because its not 'static

Josh Triplett (Apr 02 2020 at 23:05, on Zulip):

And context versus with_context isn't the primary source of my verbosity. It's changing the one character ? into .with_context(context)?.

Josh Triplett (Apr 02 2020 at 23:05, on Zulip):

The only other language-level thing that would help would be the ability to say "the rest of this block is wrapped in this macro", which would help with formatting and indentation, but is by no means required.

Josh Triplett (Apr 02 2020 at 23:06, on Zulip):

(And could technically be done by a proc macro attached to the function, though, ow.)

Jane Lusby (Apr 02 2020 at 23:06, on Zulip):

yea, i think you should experiment with tracing personally

Jane Lusby (Apr 02 2020 at 23:06, on Zulip):

because if that works its everything you want

Jane Lusby (Apr 02 2020 at 23:06, on Zulip):

you just enter the span and it uses RAII to drop the context when it goes out of scope

Josh Triplett (Apr 02 2020 at 23:06, on Zulip):

I'll keep that in mind; it does sound nice.

Jane Lusby (Apr 02 2020 at 23:06, on Zulip):

and then you just have to get anyhow (or eyre which already can do this) to capture the SpanTrace when its constructed

Jane Lusby (Apr 02 2020 at 23:07, on Zulip):

beyond that I don't have any ready solutions for you other than a macro that creates a nice context object given some fields which can then be passed into .with_context()

Josh Triplett (Apr 02 2020 at 23:07, on Zulip):

I appreciate you taking the time to ponder this problem.

Jane Lusby (Apr 02 2020 at 23:08, on Zulip):

my pleasure

Josh Triplett (Apr 02 2020 at 23:08, on Zulip):

And I do hope that we get something satisfying into the standard library. I appreciate you helping to drive error-handling forward.

Jane Lusby (Apr 02 2020 at 23:08, on Zulip):

any excuse to get familiar with more error handling use cases

Jane Lusby (Apr 02 2020 at 23:08, on Zulip):

ty ^_^

Jane Lusby (Apr 02 2020 at 23:08, on Zulip):

yea i think the error reporting part (something like anyhow) will probably be the last thing to get into std

Jane Lusby (Apr 02 2020 at 23:08, on Zulip):

also im surprised you're using anyhow at all in a perf critical piece of code

Jane Lusby (Apr 02 2020 at 23:09, on Zulip):

but i might have bad intuition about the cost of allocations

Josh Triplett (Apr 02 2020 at 23:09, on Zulip):

@Jane Lusby The non-error path is performance critical, but it doesn't allocate.

Jane Lusby (Apr 02 2020 at 23:09, on Zulip):

aaaaah

Jane Lusby (Apr 02 2020 at 23:09, on Zulip):

that makes sense

Josh Triplett (Apr 02 2020 at 23:09, on Zulip):

The error path is emphatically not performance-critical.

Jane Lusby (Apr 02 2020 at 23:09, on Zulip):

i should internalize that better

Jane Lusby (Apr 02 2020 at 23:09, on Zulip):

that should be obvious

Josh Triplett (Apr 02 2020 at 23:10, on Zulip):

Yeah, it took me a long time.

Josh Triplett (Apr 02 2020 at 23:10, on Zulip):

I was allergic to anything even close to Box<dyn Error> for the longest time.

Josh Triplett (Apr 02 2020 at 23:10, on Zulip):

Then I finally started realizing how much overhead an enum error type had.

Jane Lusby (Apr 02 2020 at 23:11, on Zulip):

is it more?

Josh Triplett (Apr 02 2020 at 23:11, on Zulip):

In size, very much so.

Jane Lusby (Apr 02 2020 at 23:11, on Zulip):

or just not much less

Jane Lusby (Apr 02 2020 at 23:11, on Zulip):

ah yes

Jane Lusby (Apr 02 2020 at 23:11, on Zulip):

especially if they get deep

Jane Lusby (Apr 02 2020 at 23:11, on Zulip):

and that would impact the hot path too

Josh Triplett (Apr 02 2020 at 23:11, on Zulip):

Deep or embedding lots of useful error context that itself includes allocations anyway (like a string).

Josh Triplett (Apr 02 2020 at 23:12, on Zulip):

And exactly.

Jane Lusby (Apr 02 2020 at 23:12, on Zulip):

where as anyhow is 1 ptr wide on the stack

Jane Lusby (Apr 02 2020 at 23:12, on Zulip):

makes sense

Josh Triplett (Apr 02 2020 at 23:12, on Zulip):

That was the realization that got me to like anyhow very much: that I don't care about the performance of the error path, I care about the performance of the non-error path, and I was hurting it by using custom error types.

Josh Triplett (Apr 02 2020 at 23:13, on Zulip):

And in particular that Result<T, MyCarefullyConstructedError> was going to be much much bigger than T for almost any T I had.

Jane Lusby (Apr 02 2020 at 23:13, on Zulip):

yea

Jane Lusby (Apr 02 2020 at 23:13, on Zulip):

anyways

Jane Lusby (Apr 02 2020 at 23:14, on Zulip):

try out tracing error, https://github.com/tokio-rs/tracing/blob/master/examples/examples/custom_error.rs this should be a good example to use to get the necessary pieces

Josh Triplett (Apr 02 2020 at 23:14, on Zulip):

(The other allergy I had to dyn Error and anyhow was this ingrained systems-programming concept of "you can't allocate on the error path! What happens if something goes wrong with allocation!".)

Jane Lusby (Apr 02 2020 at 23:14, on Zulip):

though that uses #[instrument] instead of ever invoking the span macros directly

Jane Lusby (Apr 02 2020 at 23:14, on Zulip):

ha yea, one of my friends has to do zero allocation in the error path for that exact reason

Jane Lusby (Apr 02 2020 at 23:14, on Zulip):

tho hes doing os work

Josh Triplett (Apr 02 2020 at 23:15, on Zulip):

(To which I have to keep reminding myself that I'm not writing an operating system or a memory allocator, and if allocation fails I have much worse problems.)

Jane Lusby (Apr 02 2020 at 23:15, on Zulip):

lol

Jane Lusby (Apr 02 2020 at 23:17, on Zulip):

ill think more about the context stuff incase tracing doesnt work out and ill let you know if I think of anything

Jane Lusby (Apr 02 2020 at 23:18, on Zulip):

if we just had postfix macros...

Josh Triplett (Apr 02 2020 at 23:25, on Zulip):

How might postfix macros help here? Note that I want to write ?, not .c!()? or similar.

Josh Triplett (Apr 02 2020 at 23:25, on Zulip):

(I want postfix macros too, but I'm wondering how you see them being used in this context.)

Jane Lusby (Apr 02 2020 at 23:25, on Zulip):

oh i was thinking if you could define a postfix macro that replaced context and called it internally and also inserted your context that it would accomplish your needs

Jane Lusby (Apr 02 2020 at 23:26, on Zulip):

so there would be one macro call that basically defines the scope and maybe also defines the context macro and creates the closure to format in the members

Josh Triplett (Apr 02 2020 at 23:27, on Zulip):

Honestly, it isn't just verbosity that prompted me to want this. It's "oops, I almost forgot what was in my let context = || ...; and used the wrong context for that error, this needs to be more automatic and scoped".

Jane Lusby (Apr 02 2020 at 23:28, on Zulip):

hmmm

Jane Lusby (Apr 02 2020 at 23:29, on Zulip):

so the thing you're afraid of is this

Jane Lusby (Apr 02 2020 at 23:29, on Zulip):
fn blah(local: i32, local2: i32) -> Result<(), Error> {
    error_context!(local);

    fallible_fn().context!("the error message")?;

    // error_context!(local2); OOPS forgot to add

    fallible_fn2().context!("the error message2")?;
}
Josh Triplett (Apr 02 2020 at 23:29, on Zulip):

In terms of error_context! yes.

Jane Lusby (Apr 02 2020 at 23:30, on Zulip):

but you dont want to add blocks with indentation

Jane Lusby (Apr 02 2020 at 23:30, on Zulip):

or is it specifically closures that you dont want

Josh Triplett (Apr 02 2020 at 23:31, on Zulip):

I don't mind adding blocks with indentation, though I'd love to not have to.

Jane Lusby (Apr 02 2020 at 23:31, on Zulip):
fn blah(local: i32, local2: i32) -> Result<(), Error> {
    {
        error_context!(local);

        fallible_fn().context!("the error message")?;
    }

    // error_context!(local2); OOPS forgot to add

    fallible_fn2().context!("the error message2")?; // OOPS compiler error, context! not defined, yay!
}
Josh Triplett (Apr 02 2020 at 23:31, on Zulip):

What almost happened was this:

Josh Triplett (Apr 02 2020 at 23:33, on Zulip):

In this case, it was:

fn walk_tree(tree: Tree, ...) {
    let context = || format!("Processing tree {}", tree.id());
    ...
    for entry in tree {
        ...
        match mode {
            ...
            FileMode::Tree => {
                ...
                some_call().with_context(context)?; // OOPS this context is the tree I'm walking not the subtree I'm about to recurse into
Jane Lusby (Apr 02 2020 at 23:33, on Zulip):

because you had some other error in the top level that could throw before the entry iteration

Josh Triplett (Apr 02 2020 at 23:34, on Zulip):

Right.

Josh Triplett (Apr 02 2020 at 23:34, on Zulip):

This is a long function that has calls with ? on every other line.

Josh Triplett (Apr 02 2020 at 23:34, on Zulip):

Most of which will produce inscrutable errors without some idea of "this happened while I was processing a tree".

Jane Lusby (Apr 02 2020 at 23:34, on Zulip):

yea, even with tracing error if you forget to add the span for the entry then you'll only get the context for the tree, the difference is it grabs all active spans when it gets the span trace

Jane Lusby (Apr 02 2020 at 23:35, on Zulip):

so if you have the context at the top level and the context for the entry you'll get them each as different frames essentially

Jane Lusby (Apr 02 2020 at 23:35, on Zulip):

to borrow backtrace parlance

Jane Lusby (Apr 02 2020 at 23:36, on Zulip):

and unless the top level context is never used after the for loop you're gonna have trouble scoping that

Jane Lusby (Apr 02 2020 at 23:36, on Zulip):

or in any situation where the scope for one context completely contains the scope of another

Jane Lusby (Apr 02 2020 at 23:37, on Zulip):

im not sure if that problem is solvable

Jane Lusby (Apr 02 2020 at 23:37, on Zulip):

:/

centril (Apr 02 2020 at 23:43, on Zulip):

Josh Triplett said:

And in particular that Result<T, MyCarefullyConstructedError> was going to be much much bigger than T for almost any T I had.

Oh yeah; in the compiler we basically do pub struct DiagnosticBuilder<'a>(Box<DiagnosticBuilderInner<'a>>); and type PResult<'a, T> = Result<T, DiagnosticBuilder<'a>>; where PResult<'a, T> is the error type used all over the place.

centril (Apr 02 2020 at 23:43, on Zulip):

boxing that up had some nice perf wins

centril (Apr 02 2020 at 23:44, on Zulip):

(DiagnosticBuilderInner is a pretty large type, https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/diagnostic_builder/struct.DiagnosticBuilderInner.html)

Jane Lusby (Apr 02 2020 at 23:45, on Zulip):

In theory, return value optimization (RVO) should avoid unnecessary copying. In practice, it does not (at the time of writing).

Jane Lusby (Apr 02 2020 at 23:45, on Zulip):

is this still the case? :(

Jane Lusby (Apr 02 2020 at 23:49, on Zulip):

@Josh Triplett eliza wants to know if you're using log

David Barsky (Apr 03 2020 at 00:24, on Zulip):

Hi! Few notes on tracing:

Josh Triplett (Apr 03 2020 at 00:31, on Zulip):

@Jane Lusby Did you send a message that @-ed me, and then edit it? I had a notification and now can't find the corresponding message.

Jane Lusby (Apr 03 2020 at 00:33, on Zulip):

yea,

Jane Lusby (Apr 03 2020 at 00:33, on Zulip):

eliza was asking if you're using log currently but I didnt think it was relevant after interrogating her about why she wanted to know

Jane Lusby (Apr 03 2020 at 00:33, on Zulip):

so i did my best to delete it :<

Jane Lusby (Apr 03 2020 at 00:33, on Zulip):

thats the ~ msg

Josh Triplett (Apr 03 2020 at 19:23, on Zulip):

Ah, makes sense.

Josh Triplett (Apr 03 2020 at 19:23, on Zulip):

Thanks.

nikomatsakis (Apr 15 2020 at 18:41, on Zulip):

I didn't read this topic

nikomatsakis (Apr 15 2020 at 18:42, on Zulip):

Did I miss anything? :)

Josh Triplett (Apr 15 2020 at 18:42, on Zulip):

You might be interested to read it. It was partly inspired by your comments about trying out fehler and thinking about error-handling patterns.

Josh Triplett (Apr 15 2020 at 18:44, on Zulip):

tl;dr: in the course of writing code with error-handling, I ran into a case that feels like it could use some language-level help, namely providing context for errors to clarify an error that would otherwise get propagated with ? without enough explanation to make sense of it.

nikomatsakis (Apr 15 2020 at 18:45, on Zulip):

Ah. On a related note I would very much like some procedural macros to automatically give context from functions. @Yoshua Wuyts had some interesting libraries here.

Josh Triplett (Apr 15 2020 at 18:47, on Zulip):

I do think that's a good idea. But that's also often not sufficient for human-readable errors.

Josh Triplett (Apr 15 2020 at 18:48, on Zulip):

For instance, I might need to do a bit of computation to give a more helpful error message, and I can't do that computation until a little bit into the function, so failures after that point should include that information.

simulacrum (Apr 15 2020 at 20:37, on Zulip):

I think this also has interesting parallels to my desire for some sort of context aware Debug and such, too, along with potentially moving things like async executors out of TLS ergonomically. Generally "context" that you want to thread through your program without passing explicitly, and likely through things like iterator adapters where it's not even possible much of the time

Josh Triplett (Apr 15 2020 at 20:40, on Zulip):

Yeah. I feel like I do actually want something like try { ... }.with_context(|| compute and provide more context)? , added "on the way out" of a scope. The one addition is that I'd like to be able to "wrap the rest of the function" in that, rather than it being a purely nesting block.

Josh Triplett (Apr 15 2020 at 20:41, on Zulip):

The former is writable with no language additions other than try. The latter would need a language change, or a proc macro.

XAMPPRocky (Apr 15 2020 at 22:38, on Zulip):

@Josh Triplett Have you tried snafu? It has some proc macro magic to let you easily pass and format context into your errors. https://docs.rs/snafu

#[derive(Debug, Snafu)]
enum Error {
    #[snafu(display("Could not open config from {}: {}", filename.display(), source))]
    OpenConfig {
        filename: PathBuf,
        source: std::io::Error,
    },
    #[snafu(display("Could not save config to {}: {}", filename.display(), source))]
    SaveConfig {
        filename: PathBuf,
        source: std::io::Error,
    },
    #[snafu(display("The user id {} is invalid", user_id))]
    UserIdInvalid { user_id: i32, backtrace: Backtrace },
}

type Result<T, E = Error> = std::result::Result<T, E>;

fn log_in_user<P>(config_root: P, user_id: i32) -> Result<bool>
where
    P: AsRef<Path>,
{
    let config_root = config_root.as_ref();
    let filename = &config_root.join("config.toml");

    let config = fs::read(filename).context(OpenConfig { filename })?;
    // Perform updates to config
    fs::write(filename, config).context(SaveConfig { filename })?;

    ensure!(user_id == 42, UserIdInvalid { user_id });

    Ok(true)
}
Josh Triplett (Apr 16 2020 at 06:37, on Zulip):

@XAMPPRocky I'm not sure what I'm supposed to be seeing there. Most of that looks very similar to thiserror. And that doesn't seem to solve the context problem at all. It just provides context functions; they still need calling before every ?.

XAMPPRocky (Apr 16 2020 at 09:36, on Zulip):

Ah, I only saw the context discussion. Not the “provide context without calling context”.

Last update: Jun 05 2020 at 23:10UTC