locate-dwarf icon indicating copy to clipboard operation
locate-dwarf copied to clipboard

anyhow Errors make it difficult to use this crate

Open schultetwin1 opened this issue 3 years ago • 8 comments

I'm trying to integrate locate-dwarf into my own crate but the usage of anyhow has made it difficult for me to make decisions based on the errors returned. For example, I want to take a different action if an io error occurred in locate_debug_symbols versus if there is no debug pointer to be found.

I'd be willing to change this crate to use thiserror if you are open to it?

schultetwin1 avatar Apr 27 '21 15:04 schultetwin1

Yes, would be better to return a Result<Option<PathBuf>> as debug info not being available isn't a fatal error.

dvc94ch avatar Apr 27 '21 15:04 dvc94ch

I'd be willing to change this crate to use thiserror if you are open to it?

I had originally used failure, but thiserror is absolutely more useful for library crates.

luser avatar Apr 28 '21 13:04 luser

I had originally used failure, but thiserror is absolutely more useful for library crates.

That is actually a myth in most cases. The way thiserror is usually abused people put all the possible errors that any code path in a crate could have in an enum and then return it from everywhere. The main argument for thiserror is handling all possible errors and have the compiler check that all cases are handled. Which is a bad argument for one because not all errors are possible in every code path and the wrapping and rewrapping of errors (each library has at least an Io variant) actually makes it harder, and most ways of handling errors involve one of retrying/logging/aborting. And there are a whole host of other reasons why that "rule of thumb" brainlessly applied yields worse results than just using anyhow. You need to capture your backtraces manually which absolutely no one does, it's slower, etc.

And in this case the library is so trivial that an Option is a much better solution anyway. However where thiserror shines is in conveniently deriving Display.

EDIT: sorry if it came of as a little aggressive, I just don't like "wisdom of the crowd" being justified by it's "the wisdom of the crowd". I'm happy to hear why thiserror is perfectly suited for this crate.

dvc94ch avatar Apr 28 '21 13:04 dvc94ch

sorry if it came of as a little aggressive, I just don't like "wisdom of the crowd" being justified by it's "the wisdom of the crowd". I'm happy to hear why thiserror is perfectly suited for this crate.

No worries, I understand thiserror makes it super easy for someone to just add in all possible errors. That's not what I'm suggesting. I just like thiserror for two reasons (one of which you mentioned).

  1. It derives Display for you
  2. It makes using source very easy (ok... this may be exactly proving your point, but in some cases it's very useful)

And in this case the library is so trivial that an Option is a much better solution anyway.

I started down this path but there are a couple errors that are explicitly handled in this crate. I believe they are ok to switch to Option but would love feedback on that decision. Specifically they are:

  1. The given build-id to locate_debug_build_id is too short.
  2. The parameter path passed into locate_gnu_debuglink can not be canonicalize'd or does not have a parent.
  3. The object::Object variable in locate debug symbols has some error when attempting to read mach_uuid, build_id, or gnu_debuglink.
  4. Rust can't convert the debuglink path to a real path.

For case 1, if the buildid is less than 2 bytes we should return None because it wouldn't be in a .build-id folder anyway. For case 2, we can return None since a path that did not have a parent would not work for all those searches anyway. For case 3, these will all occur if the passed in object was corrupted in some way. It might be nice for the user to know that but we could also say " if that data is corrupted, we can't find the dwarf anyway For case 4, if the path is invalid UTF-8, we won't be able to find the dwarf anyway.

schultetwin1 avatar Apr 29 '21 09:04 schultetwin1

I'd suggest returning an error if there is corruption or a transient error and None if it isn't found. A transient error would be an io error that could just be ?. For the corruption case it may be worth enumerating the cases like this:

#[derive(Debug, Error)]
pub enum CorruptObject {
    #[error("build id is too short")]
    BuildIdTooShort,
    ...
}

This way if someone really wants to handle the corrupt/transient errors differently they can use downcast_ref::<CorruptObject> or downcast_ref::<std::io::Error> and backtraces are still captured (as the return type would be an anyhow::Result).

dvc94ch avatar Apr 29 '21 10:04 dvc94ch

I've created a PR #49 that partially completes this work. It adds in the Option to the success case of the returned path. But it does not include the custom error object. I'm working on that next.

schultetwin1 avatar May 05 '21 03:05 schultetwin1

FWIW, having just run into a similar issue attempting to use this crate for a small tool, I can say that at a bare minimum whatever error type this crate returns needs to be exported as part of the public API. At present it's not possible to refer to locate_dwarf::Error.

A bit late, but in re:

I had originally used failure, but thiserror is absolutely more useful for library crates.

That is actually a myth in most cases

I simply meant "failure is unmaintained, thiserror is under active development".

To the original point of this issue: anyhow seems intended for application usage, not library usage, for the "roll all errors up into a single error type" use case. I'm not particularly hung up on this.

luser avatar Sep 14 '21 19:09 luser

Not really that bothered since I'm not currently using locate-dwarf. But you're not going to convince me that thiserror is better for library crates than anyhow for various reasons I mentioned. It may be that in some cases it's a better fit when done correctly. But if you're just wrapping every error from every dependency in a new enum it's not the case. And please make sure that at least the backtrace is captured and unwrapping an error actually displays were the error was constructed and not where it was unwrapped.

dvc94ch avatar Sep 14 '21 19:09 dvc94ch