Skip to content

Instantly share code, notes, and snippets.

@DrPlantabyte
Created July 3, 2023 02:32
Show Gist options
  • Save DrPlantabyte/367ad6fc02ce8a01e7c28f87533dda31 to your computer and use it in GitHub Desktop.
Save DrPlantabyte/367ad6fc02ce8a01e7c28f87533dda31 to your computer and use it in GitHub Desktop.
HOW TO IMPLEMENT JAVA-STYLE EXCEPTIONS IN IDIOMATIC RUST
/*
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