-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Return the custom error instead of its cause in io::Error::{cause,source}
#101818
Conversation
(rust-highfive has picked a reviewer for you, use r? to override) |
Hey! It looks like you've submitted a new PR for the library teams! If this PR contains changes to any Examples of
|
This needs FCP, and I'm not sure it's actually a good idea. |
I think this is a "damned if we do damned if we don't" kind of situation. Currently, I'd also like to re-iterate the argument from #101817 that stabilizing #99262 would mean that |
It's also |
Since the implementation is pretty simple, and this is mostly a decision about whether or not we should do this (and if we should it needs an FCP) I'm going to reassign to yaahc. r? @yaahc |
Oh, I saw the original issue but I hadn't seen this PR. I agree with the discussion in the issue that we probably don't want to make this change, and that to access the inner error the I'd recommend experimenting with potential new APIs to make it easier to identify errors in source chains. For a while now I've wanted an equivalent of #![feature(error_iter)]
use std::error::Error;
use std::io;
pub fn are_any<E: Error + 'static>(err: &(dyn Error + 'static)) -> bool {
err.sources()
.map(|e| {
e.downcast_ref::<io::Error>()
.and_then(|e| e.get_ref().map(|e| e as _))
.unwrap_or(e)
})
.any(|e| e.is::<E>())
} edit: looking at it more I think the middle map is the one that we could maybe abstract into its own function... cleaned up version: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=058d3c0b6fb75b990cf05d48166245e0 #![feature(error_iter)]
use std::error::Error;
use std::io;
pub fn are_any<E: Error + 'static>(err: &(dyn Error + 'static)) -> bool {
err.sources()
.map(unwrap_if_io_error)
.any(|e| e.is::<E>())
}
// hopefully obvious this would need a better name
fn unwrap_if_io_error<'a>(e: &'a (dyn Error + 'static)) -> &'a (dyn Error + 'static) {
e.downcast_ref::<io::Error>()
.and_then(|e| e.get_ref().map(|e| e as _))
.unwrap_or(e)
} The |
Yeah, I agree this doesn't seem like the right approach. |
I think what makes it tricky to require the wrapper is that you want it to apply recursively. So if you have SomeError(io::Error::Custom(SomeOtherError(io::Error::Custom(InnerError))))))))) You can still get at |
Hundred percent agree that it feels weird, but I feel like having it be an implicit behavior as part of a more general API feels even more magical and spooky. Maybe in this case it's core enough to the language to justify it, but I can't help but think that I feel like if this were in cargo or one of the tools I'd happily choose the second option in the spirit of "make the happy path easy and the edge cases possible", but within the standard library the special casing still feels very spooky to me. That's just me though, it might be worth nominating for discussion in the next libs-api meeting to get more perspectives on how everyone else feels.
I'm confused on this one. It seems like the example you gave would work with the snippet I posted. I think if it were an Which can be fixed with... fn unwrap_if_io_error<'a>(mut e: &'a (dyn Error + 'static)) -> &'a (dyn Error + 'static) {
while e.is::<io::Error>() {
e = e.downcast_ref::<io::Error>()
.and_then(|e| e.get_ref().map(|e| e as _))
.unwrap();
}
e
} That said, I can't imagine why someone would wrap an |
Heh, yeah, I'm very torn here on whether this is
Ah, I misremembered and extrapolated from
Unfortunately, exactly that can pretty easily happen inadvertently through libraries that are generic over I/O. For example, this happens when using hyper on top of rustls: #101817 (comment). So I think the I also think such a helper should probably include both the |
I know of a few codebases that have replicated similar patterns to io::Error. That said, for those cases it's probably easier to justify changing source/cause. |
Fixes #101817
One note about this that I realize now, is that this will result in an error that formats the same being hit twice in the cause chain. So I'm unsure this is actually desirable.