Rollup merge of #98387 - NobodyXu:feature/std_io_Error_try_downgrade_inner, r=yaahc
Add new unstable API `downcast` to `std::io::Error` https://github.com/rust-lang/libs-team/issues/57 Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
This commit is contained in:
commit
bf9ed99496
@ -795,6 +795,68 @@ pub fn into_inner(self) -> Option<Box<dyn error::Error + Send + Sync>> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to downgrade the inner error to `E` if any.
|
||||
///
|
||||
/// If this [`Error`] was constructed via [`new`] then this function will
|
||||
/// attempt to perform downgrade on it, otherwise it will return [`Err`].
|
||||
///
|
||||
/// If downgrade succeeds, it will return [`Ok`], otherwise it will also
|
||||
/// return [`Err`].
|
||||
///
|
||||
/// [`new`]: Error::new
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(io_error_downcast)]
|
||||
///
|
||||
/// use std::fmt;
|
||||
/// use std::io;
|
||||
/// use std::error::Error;
|
||||
///
|
||||
/// #[derive(Debug)]
|
||||
/// enum E {
|
||||
/// Io(io::Error),
|
||||
/// SomeOtherVariant,
|
||||
/// }
|
||||
///
|
||||
/// impl fmt::Display for E {
|
||||
/// // ...
|
||||
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
/// # todo!()
|
||||
/// # }
|
||||
/// }
|
||||
/// impl Error for E {}
|
||||
///
|
||||
/// impl From<io::Error> for E {
|
||||
/// fn from(err: io::Error) -> E {
|
||||
/// err.downcast::<E>()
|
||||
/// .map(|b| *b)
|
||||
/// .unwrap_or_else(E::Io)
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "io_error_downcast", issue = "99262")]
|
||||
pub fn downcast<E>(self) -> result::Result<Box<E>, Self>
|
||||
where
|
||||
E: error::Error + Send + Sync + 'static,
|
||||
{
|
||||
match self.repr.into_data() {
|
||||
ErrorData::Custom(b) if b.error.is::<E>() => {
|
||||
let res = (*b).error.downcast::<E>();
|
||||
|
||||
// downcast is a really trivial and is marked as inline, so
|
||||
// it's likely be inlined here.
|
||||
//
|
||||
// And the compiler should be able to eliminate the branch
|
||||
// that produces `Err` here since b.error.is::<E>()
|
||||
// returns true.
|
||||
Ok(res.unwrap())
|
||||
}
|
||||
repr_data => Err(Self { repr: Repr::new(repr_data) }),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the corresponding [`ErrorKind`] for this error.
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -132,6 +132,15 @@ unsafe impl Send for Repr {}
|
||||
unsafe impl Sync for Repr {}
|
||||
|
||||
impl Repr {
|
||||
pub(super) fn new(dat: ErrorData<Box<Custom>>) -> Self {
|
||||
match dat {
|
||||
ErrorData::Os(code) => Self::new_os(code),
|
||||
ErrorData::Simple(kind) => Self::new_simple(kind),
|
||||
ErrorData::SimpleMessage(simple_message) => Self::new_simple_message(simple_message),
|
||||
ErrorData::Custom(b) => Self::new_custom(b),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn new_custom(b: Box<Custom>) -> Self {
|
||||
let p = Box::into_raw(b).cast::<u8>();
|
||||
// Should only be possible if an allocator handed out a pointer with
|
||||
|
@ -10,6 +10,10 @@
|
||||
pub(super) struct Repr(Inner);
|
||||
|
||||
impl Repr {
|
||||
#[inline]
|
||||
pub(super) fn new(dat: ErrorData<Box<Custom>>) -> Self {
|
||||
Self(dat)
|
||||
}
|
||||
pub(super) fn new_custom(b: Box<Custom>) -> Self {
|
||||
Self(Inner::Custom(b))
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{const_io_error, Custom, Error, ErrorData, ErrorKind, Repr};
|
||||
use super::{const_io_error, Custom, Error, ErrorData, ErrorKind, Repr, SimpleMessage};
|
||||
use crate::assert_matches::assert_matches;
|
||||
use crate::error;
|
||||
use crate::fmt;
|
||||
@ -141,3 +141,54 @@ fn test_custom_error_packing() {
|
||||
}) if error.downcast_ref::<Bojji>().as_deref() == Some(&Bojji(true)),
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct E;
|
||||
|
||||
impl fmt::Display for E {
|
||||
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for E {}
|
||||
|
||||
#[test]
|
||||
fn test_std_io_error_downcast() {
|
||||
// Case 1: custom error, downcast succeeds
|
||||
let io_error = Error::new(ErrorKind::Other, Bojji(true));
|
||||
let e: Box<Bojji> = io_error.downcast().unwrap();
|
||||
assert!(e.0);
|
||||
|
||||
// Case 2: custom error, downcast fails
|
||||
let io_error = Error::new(ErrorKind::Other, Bojji(true));
|
||||
let io_error = io_error.downcast::<E>().unwrap_err();
|
||||
|
||||
// ensures that the custom error is intact
|
||||
assert_eq!(ErrorKind::Other, io_error.kind());
|
||||
let e: Box<Bojji> = io_error.downcast().unwrap();
|
||||
assert!(e.0);
|
||||
|
||||
// Case 3: os error
|
||||
let errno = 20;
|
||||
let io_error = Error::from_raw_os_error(errno);
|
||||
let io_error = io_error.downcast::<E>().unwrap_err();
|
||||
|
||||
assert_eq!(errno, io_error.raw_os_error().unwrap());
|
||||
|
||||
// Case 4: simple
|
||||
let kind = ErrorKind::OutOfMemory;
|
||||
let io_error: Error = kind.into();
|
||||
let io_error = io_error.downcast::<E>().unwrap_err();
|
||||
|
||||
assert_eq!(kind, io_error.kind());
|
||||
|
||||
// Case 5: simple message
|
||||
const SIMPLE_MESSAGE: SimpleMessage =
|
||||
SimpleMessage { kind: ErrorKind::Other, message: "simple message error test" };
|
||||
let io_error = Error::from_static_message(&SIMPLE_MESSAGE);
|
||||
let io_error = io_error.downcast::<E>().unwrap_err();
|
||||
|
||||
assert_eq!(SIMPLE_MESSAGE.kind, io_error.kind());
|
||||
assert_eq!(SIMPLE_MESSAGE.message, &*format!("{io_error}"));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user