about summary refs log tree commit diff
diff options
context:
space:
mode:
authorYuki Okushi <jtitor@2k36.org>2022-07-16 17:52:59 +0900
committerGitHub <noreply@github.com>2022-07-16 17:52:59 +0900
commitbf9ed994960cee0443a39e952f1a8b59d3f48a40 (patch)
tree15668a6de306ff64910b320f903aab2f525561d2
parente6c43cf8b98e4837bbee1cab225621001a3f2230 (diff)
parent8e8a3be22f1082da965f6a696be93fbbd7b5d4ba (diff)
downloadrust-bf9ed994960cee0443a39e952f1a8b59d3f48a40.tar.gz
rust-bf9ed994960cee0443a39e952f1a8b59d3f48a40.zip
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>
-rw-r--r--library/std/src/io/error.rs62
-rw-r--r--library/std/src/io/error/repr_bitpacked.rs9
-rw-r--r--library/std/src/io/error/repr_unpacked.rs4
-rw-r--r--library/std/src/io/error/tests.rs53
4 files changed, 127 insertions, 1 deletions
diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs
index 4a50e647c64..ff7fdcae16f 100644
--- a/library/std/src/io/error.rs
+++ b/library/std/src/io/error.rs
@@ -795,6 +795,68 @@ impl Error {
         }
     }
 
+    /// 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
diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/std/src/io/error/repr_bitpacked.rs
index e80068b46ab..292bf4826fd 100644
--- a/library/std/src/io/error/repr_bitpacked.rs
+++ b/library/std/src/io/error/repr_bitpacked.rs
@@ -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
diff --git a/library/std/src/io/error/repr_unpacked.rs b/library/std/src/io/error/repr_unpacked.rs
index 3729c039c42..d6ad55b99f5 100644
--- a/library/std/src/io/error/repr_unpacked.rs
+++ b/library/std/src/io/error/repr_unpacked.rs
@@ -10,6 +10,10 @@ type Inner = ErrorData<Box<Custom>>;
 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))
     }
diff --git a/library/std/src/io/error/tests.rs b/library/std/src/io/error/tests.rs
index 8d7877bcad3..c897a5e8701 100644
--- a/library/std/src/io/error/tests.rs
+++ b/library/std/src/io/error/tests.rs
@@ -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}"));
+}