Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migration from io::Error to io::ErrrorKind #560

Closed
wants to merge 3 commits into from
Closed

Migration from io::Error to io::ErrrorKind #560

wants to merge 3 commits into from

Conversation

dr-orlovsky
Copy link
Collaborator

This completes Error derives work from #558 and #559 with API-breaking changes, including

  • Move from io::Error to io::ErrorKind type, which is Copy, Eq, Ord & Hash and is easier to handle. The cost of this is the need to get rid of deprecated Error::cause implementations, which I think is also beneficial (and at least one of Error types hadn't it anyway)
  • Renaming some of error variants, removing unused ones & improving inner type representation

@dr-orlovsky dr-orlovsky added the API break This PR requires a version bump for the next release label Jan 30, 2021
@dr-orlovsky dr-orlovsky marked this pull request as draft January 30, 2021 13:40
@dr-orlovsky dr-orlovsky added this to In Progress in Derive unification Jan 30, 2021
@stevenroose
Copy link
Collaborator

NACK. I have tried this before myself and I gave up on it. Needed those types on error types usually indicates some bad practice.

Also, giving up io::Error for ErrorKind removes a lot of potentially useful information, which I totally don't agree with.

@thomaseizinger
Copy link
Contributor

NACK cee0a54

Removing the impl of std::error::Error will make it very painful downstream to work with libraries like anyhow or eyre.

@dr-orlovsky
Copy link
Collaborator Author

Agree that impl std::error::Error is required, it's my bad that removing deprecated cause I removed the whole implementation. This is fixed.

However will argue that using io::Error instead of io::ErrorKind at expense of having errors non-clonable, non-equatable, non-orderable and non-hashable is a bad practice. There a big discussion in rust community going on regarding the io::Error being bad practice and probably @Kixunil has some strong arguments about it.

Copy link
Contributor

@thomaseizinger thomaseizinger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However will argue that using io::Error instead of io::ErrorKind at expense of having errors non-clonable, non-equatable, non-orderable and non-hashable is a bad practice. There a big discussion in rust community going on regarding the io::Error being bad practice and probably @Kixunil has some strong arguments about it.

I am happy to hear more arguments for this! In my current experience, errors shouldn't usually need be clonable etc and I am worried that removing the inner IO error from cause for example makes use lose viable information.

@@ -589,7 +586,7 @@ impl ExtendedPrivKey {
network: network,
key: secp256k1::SecretKey::from_slice(
&data[46..78]
).map_err(Error::Ecdsa)?,
).map_err(Error::Secp256k1)?,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that we have a From<secp256k1::Error> for Error implementation, I think this map_err is redundant because ? calls Error::from for you.

@@ -212,9 +212,7 @@ impl Map for Global {
entry.insert((fingerprint1, derivation1));
continue
}
return Err(psbt::Error::MergeConflict(format!(
"global xpub {} has inconsistent key sources", xpub
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if it is intentional but we lost this part of the error message.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not: we have a dedicated error type which may happen only for a global xpub (and we embed xpub into error)

fn cause(&self) -> Option<&dyn error::Error> {
match *self {
Error::Io(ref e) => Some(e),
Error::Psbt(ref e) => Some(e),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

psbt::Error is still a valid cause, I think we should keep that!

@Kixunil
Copy link
Collaborator

Kixunil commented Apr 7, 2021

Indeed, as I said io::Error attempts to be both low-level and high-level while failing at both. Sadly, this PR doesn't address my issues with io::Error but makes them worse instead.

The main reason for my criticism of io::Error is lack of structure and missing information in common scenarios. The simplest example is that if you write a trivial version of cp command in Rust and naively use ? operator the user experience will be worse than .unwrap() method because of missing paths in the error message (which of the two files failed?). The issue worsens with the complexity of the program and leads to debugging nightmares. I was affected by such debugging nightmares multiple times so my rule of thumb is to put a lot of information into error.

It is possible for a motivated programmer to wrap IO errors so that they would contain the path. (I do this often, creating my own error type.) This is not translatable and not no_std but at least better than lack of information in common cases. This PR would invalidate such effort as it causes the additional information to be lost in conversions.

While I would love to see Rust crates to move away from io::Error it can't be done reasonably without having a better alternative to io::{Read, BufRead, Write}. I started updating genio recently but it's not going as fast as I was hoping. I can try juggle my time more if there's interest in it.

So, it's NACK from me as it's written now.

Regarding cause() - it's been replaced with source and source really, really, needs to be implemented where there's an underlying error - it will lead to incorrect messages otherwise. I know this may be in conflict with MSRV but I strongly believe that clearly documented feature flag is the correct solution here if older MSRV is desired.

errors shouldn't usually need be clonable etc

I know of at least one case when it does make sense to make errors clonable: logging into multiple sinks or logging (on another thread) and simultaneously sending a response. I actually was in a situation where this was needed. Although I managed to workaround lack of cloning, I can imagine a situation where such warkaround isn't impossible. Errors should be pretty much PODs, maybe boxed, but still PODs. But as I explained above, this PR has serious downsides.

@dr-orlovsky dr-orlovsky linked an issue May 4, 2021 that may be closed by this pull request
@dr-orlovsky dr-orlovsky added this to the 0.27.0 milestone Jun 8, 2021
@dr-orlovsky dr-orlovsky marked this pull request as ready for review June 8, 2021 06:40
@dr-orlovsky dr-orlovsky changed the title Fix/error derives 3 Migration from io::Error to io::ErrrorKind Jun 27, 2021
@dr-orlovsky
Copy link
Collaborator Author

Closed. All non-controversial parts are moved to #625

Derive unification automation moved this from In Progress to Done Jun 27, 2021
@RCasatta
Copy link
Collaborator

RCasatta commented Aug 2, 2021

source really, really, needs to be implemented where there's an underlying error

I do agree to do this, however, the guidelines recommend it only if Display isn't already printing the wrapped error rust-lang/project-error-handling#27 (comment)

@Kixunil
Copy link
Collaborator

Kixunil commented Aug 2, 2021

@RCasatta yes, we need to change both. :)

apoelstra added a commit that referenced this pull request Sep 8, 2021
994079b Refactoring error variants: removing unused; better names & inner types (Dr Maxim Orlovsky)

Pull request description:

  Removes controversial aspects from #560 (all `io::Error`-related changes) and leaves the rest

ACKs for top commit:
  sanket1729:
    ACK 994079b
  apoelstra:
    ACK 994079b

Tree-SHA512: 020e49193c885e862f45e5f7baabf1d22a3ec09e78fd7f573b2f3d327beb4f91683951ba080b3d804e8337a188dcad0f38ba70ee8059aef0681a0b2bba0a2140
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
API break This PR requires a version bump for the next release
Projects
Development

Successfully merging this pull request may close these issues.

Define and enforce standard derives
5 participants