Skip to content

Commit bf9ed99

Browse files
authored
Rollup merge of rust-lang#98387 - NobodyXu:feature/std_io_Error_try_downgrade_inner, r=yaahc
Add new unstable API `downcast` to `std::io::Error` rust-lang/libs-team#57 Signed-off-by: Jiahao XU <[email protected]>
2 parents e6c43cf + 8e8a3be commit bf9ed99

File tree

4 files changed

+127
-1
lines changed

4 files changed

+127
-1
lines changed

library/std/src/io/error.rs

+62
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,68 @@ impl Error {
795795
}
796796
}
797797

798+
/// Attempt to downgrade the inner error to `E` if any.
799+
///
800+
/// If this [`Error`] was constructed via [`new`] then this function will
801+
/// attempt to perform downgrade on it, otherwise it will return [`Err`].
802+
///
803+
/// If downgrade succeeds, it will return [`Ok`], otherwise it will also
804+
/// return [`Err`].
805+
///
806+
/// [`new`]: Error::new
807+
///
808+
/// # Examples
809+
///
810+
/// ```
811+
/// #![feature(io_error_downcast)]
812+
///
813+
/// use std::fmt;
814+
/// use std::io;
815+
/// use std::error::Error;
816+
///
817+
/// #[derive(Debug)]
818+
/// enum E {
819+
/// Io(io::Error),
820+
/// SomeOtherVariant,
821+
/// }
822+
///
823+
/// impl fmt::Display for E {
824+
/// // ...
825+
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
826+
/// # todo!()
827+
/// # }
828+
/// }
829+
/// impl Error for E {}
830+
///
831+
/// impl From<io::Error> for E {
832+
/// fn from(err: io::Error) -> E {
833+
/// err.downcast::<E>()
834+
/// .map(|b| *b)
835+
/// .unwrap_or_else(E::Io)
836+
/// }
837+
/// }
838+
/// ```
839+
#[unstable(feature = "io_error_downcast", issue = "99262")]
840+
pub fn downcast<E>(self) -> result::Result<Box<E>, Self>
841+
where
842+
E: error::Error + Send + Sync + 'static,
843+
{
844+
match self.repr.into_data() {
845+
ErrorData::Custom(b) if b.error.is::<E>() => {
846+
let res = (*b).error.downcast::<E>();
847+
848+
// downcast is a really trivial and is marked as inline, so
849+
// it's likely be inlined here.
850+
//
851+
// And the compiler should be able to eliminate the branch
852+
// that produces `Err` here since b.error.is::<E>()
853+
// returns true.
854+
Ok(res.unwrap())
855+
}
856+
repr_data => Err(Self { repr: Repr::new(repr_data) }),
857+
}
858+
}
859+
798860
/// Returns the corresponding [`ErrorKind`] for this error.
799861
///
800862
/// # Examples

library/std/src/io/error/repr_bitpacked.rs

+9
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,15 @@ unsafe impl Send for Repr {}
132132
unsafe impl Sync for Repr {}
133133

134134
impl Repr {
135+
pub(super) fn new(dat: ErrorData<Box<Custom>>) -> Self {
136+
match dat {
137+
ErrorData::Os(code) => Self::new_os(code),
138+
ErrorData::Simple(kind) => Self::new_simple(kind),
139+
ErrorData::SimpleMessage(simple_message) => Self::new_simple_message(simple_message),
140+
ErrorData::Custom(b) => Self::new_custom(b),
141+
}
142+
}
143+
135144
pub(super) fn new_custom(b: Box<Custom>) -> Self {
136145
let p = Box::into_raw(b).cast::<u8>();
137146
// Should only be possible if an allocator handed out a pointer with

library/std/src/io/error/repr_unpacked.rs

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ type Inner = ErrorData<Box<Custom>>;
1010
pub(super) struct Repr(Inner);
1111

1212
impl Repr {
13+
#[inline]
14+
pub(super) fn new(dat: ErrorData<Box<Custom>>) -> Self {
15+
Self(dat)
16+
}
1317
pub(super) fn new_custom(b: Box<Custom>) -> Self {
1418
Self(Inner::Custom(b))
1519
}

library/std/src/io/error/tests.rs

+52-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::{const_io_error, Custom, Error, ErrorData, ErrorKind, Repr};
1+
use super::{const_io_error, Custom, Error, ErrorData, ErrorKind, Repr, SimpleMessage};
22
use crate::assert_matches::assert_matches;
33
use crate::error;
44
use crate::fmt;
@@ -141,3 +141,54 @@ fn test_custom_error_packing() {
141141
}) if error.downcast_ref::<Bojji>().as_deref() == Some(&Bojji(true)),
142142
);
143143
}
144+
145+
#[derive(Debug)]
146+
struct E;
147+
148+
impl fmt::Display for E {
149+
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
150+
Ok(())
151+
}
152+
}
153+
154+
impl error::Error for E {}
155+
156+
#[test]
157+
fn test_std_io_error_downcast() {
158+
// Case 1: custom error, downcast succeeds
159+
let io_error = Error::new(ErrorKind::Other, Bojji(true));
160+
let e: Box<Bojji> = io_error.downcast().unwrap();
161+
assert!(e.0);
162+
163+
// Case 2: custom error, downcast fails
164+
let io_error = Error::new(ErrorKind::Other, Bojji(true));
165+
let io_error = io_error.downcast::<E>().unwrap_err();
166+
167+
// ensures that the custom error is intact
168+
assert_eq!(ErrorKind::Other, io_error.kind());
169+
let e: Box<Bojji> = io_error.downcast().unwrap();
170+
assert!(e.0);
171+
172+
// Case 3: os error
173+
let errno = 20;
174+
let io_error = Error::from_raw_os_error(errno);
175+
let io_error = io_error.downcast::<E>().unwrap_err();
176+
177+
assert_eq!(errno, io_error.raw_os_error().unwrap());
178+
179+
// Case 4: simple
180+
let kind = ErrorKind::OutOfMemory;
181+
let io_error: Error = kind.into();
182+
let io_error = io_error.downcast::<E>().unwrap_err();
183+
184+
assert_eq!(kind, io_error.kind());
185+
186+
// Case 5: simple message
187+
const SIMPLE_MESSAGE: SimpleMessage =
188+
SimpleMessage { kind: ErrorKind::Other, message: "simple message error test" };
189+
let io_error = Error::from_static_message(&SIMPLE_MESSAGE);
190+
let io_error = io_error.downcast::<E>().unwrap_err();
191+
192+
assert_eq!(SIMPLE_MESSAGE.kind, io_error.kind());
193+
assert_eq!(SIMPLE_MESSAGE.message, &*format!("{io_error}"));
194+
}

0 commit comments

Comments
 (0)