Created
July 3, 2023 02:32
-
-
Save DrPlantabyte/367ad6fc02ce8a01e7c28f87533dda31 to your computer and use it in GitHub Desktop.
HOW TO IMPLEMENT JAVA-STYLE EXCEPTIONS IN IDIOMATIC RUST
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
The contents of this file are Public Domain. You may use as you wish. | |
HOW TO IMPLEMENT JAVA-STYLE EXCEPTIONS IN IDIOMATIC RUST | |
(aka "How to have errors that don't suck") | |
Here we define a macro that creates a struct implementing the Error trait and automatically captures | |
and displays the backtrace unless running in release mode (and even then, you can specify | |
`RUST_BACKTRACE=1` to get the backtraces in release) | |
*/ | |
exception!(LogicError, "Logic violation encountered", | |
"Indicates an unexpected circumstance within the application, usually due to bad user input or a bug"); | |
exception!(IOError, "I/O error", | |
"Some kind of application IO error has occurred, such as a missing or corrupt file"); | |
exception!(NotImplemented, "Not implemented.", | |
"Indicates that the executed code branch wasn't implemented"); | |
#[macro_export] | |
macro_rules! exception { | |
// macro args | |
($name:ident, $dfltmsg:literal, $doc_str:literal) => | |
{ // begining of macro expansion | |
#[derive(Debug)] | |
#[doc=$doc_str] | |
pub struct $name{msg: String, trace: std::backtrace::Backtrace, cause: Option<Box <dyn std::error::Error>>} | |
impl std::error::Error for $name{ | |
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { | |
match &self.cause { | |
None => None, | |
Some(coz) => Some(coz.as_ref()) | |
} | |
} | |
} | |
impl core::fmt::Display for $name { | |
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | |
match &self.cause { | |
None => write!(f, "{}\n{}", &self.msg, &self.trace), | |
Some(coz) => write!(f, "{}\n{}\tcaused by: {}", &self.msg, &self.trace, &coz) | |
} | |
} | |
} | |
impl $name { | |
/// Creates a new instance of this error type | |
pub fn new() -> Self { | |
Self::create($dfltmsg.into(), None) | |
} | |
/// Creates a new error caused by another error | |
pub fn caused_by(cause: Box <dyn std::error::Error>) -> Self { | |
Self::create($dfltmsg.into(), Some(cause)) | |
} | |
/// Creates a new error from the given message | |
pub fn from_string<T>(msg: T) -> Self where T: Into<String> { | |
Self::create(msg.into(), None) | |
} | |
/// Creates a new error from the given message, caused by another error | |
pub fn from_string_caused_by<T>(msg: T, cause: Box <dyn std::error::Error>) -> Self where T: Into<String> { | |
Self::create(msg.into(), Some(cause)) | |
} | |
/// Internal constructor for creating an instance and capturing the backtrace | |
fn create(msg: String, cause: Option<Box <dyn std::error::Error>>) -> Self { | |
let mut btrace: Option<std::backtrace::Backtrace> = None; | |
#[cfg(debug_assertions)] { | |
btrace = Some(std::backtrace::Backtrace::force_capture()); // always make stack trace in debug mode! | |
} | |
#[cfg(not(debug_assertions))] { | |
btrace = Some(std::backtrace::Backtrace::capture()); | |
} | |
Self{ msg: msg, trace: btrace.unwrap(), cause: cause} | |
} | |
} | |
} // end of macro expansion | |
} use crate::exception; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment